Merge pull request #1098 from steveicarus/vpi-callback-improvements

VPI callback improvements
This commit is contained in:
martinwhitaker 2024-02-12 17:42:45 +00:00 committed by GitHub
commit 202d41a60c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 976 additions and 139 deletions

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2022 Jevin Sweval (jevinsweval@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
# include "vpi_user.h"
# include <assert.h>
#if defined(TEST_SIM_TIME)
#define TIME_TYPE vpiSimTime
#elif defined(TEST_SCALED_TIME)
#define TIME_TYPE vpiScaledRealTime
#else
#define TIME_TYPE vpiSuppressTime
#endif
#ifndef TEST_NULL_TIME
static s_vpi_time cb_timerec = {TIME_TYPE, 0, 0, 0};
#endif
static PLI_INT32 register_nextsimtime(struct t_cb_data* cb);
static PLI_INT32 nextsimtime_cb(struct t_cb_data* cb) {
s_vpi_time timerec;
#ifdef TEST_NULL_TIME
(void)cb;
#else
assert(cb->time && (cb->time->type == TIME_TYPE));
#endif
#if defined(TEST_SCALED_TIME) || defined(TEST_SIM_TIME)
timerec = *(cb->time);
#else
timerec.type = vpiSimTime;
vpi_get_time(NULL, &timerec);
#endif
#ifdef TEST_SCALED_TIME
vpi_printf("nextsimtime: %f\n", timerec.real);
#else
uint64_t time = ((uint64_t)timerec.high << 32) | timerec.low;
vpi_printf("nextsimtime: %" PLI_UINT64_FMT "\n", time);
#endif
register_nextsimtime(NULL);
return 0;
}
static PLI_INT32 register_nextsimtime(struct t_cb_data* cb) {
(void)cb; /* unused */
s_cb_data cb_data;
#ifdef TEST_NULL_TIME
cb_data.time = 0;
#else
cb_data.time = &cb_timerec;
#endif
#ifdef TEST_SCALED_TIME
cb_data.obj = vpi_handle_by_name("main", NULL);
#else
cb_data.obj = 0;
#endif
cb_data.reason = cbNextSimTime;
cb_data.cb_rtn = nextsimtime_cb;
vpiHandle hndl = NULL;
assert((hndl = vpi_register_cb(&cb_data)) && vpi_free_object(hndl));
return 0;
}
static void register_functions(void)
{
s_cb_data cb_data;
cb_data.reason = cbEndOfCompile;
cb_data.cb_rtn = register_nextsimtime;
vpi_register_cb(&cb_data);
}
void (*vlog_startup_routines[])(void) = {
register_functions,
NULL
};

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2022 Jevin Sweval (jevinsweval@gmail.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
`timescale 1s/1ms
module main;
initial begin
$display("time 0: %0d", $time);
#1 $display("time 1: %0d", $time);
#3 $display("time 4: %0d", $time);
#1;
end
endmodule // main

View File

@ -0,0 +1,2 @@
#define TEST_SIM_TIME
#include "nextsimtime_cb.c"

View File

@ -0,0 +1 @@
`include "vpi/nextsimtime_cb.v"

View File

@ -0,0 +1,2 @@
#define TEST_SCALED_TIME
#include "nextsimtime_cb.c"

View File

@ -0,0 +1 @@
`include "vpi/nextsimtime_cb.v"

View File

@ -0,0 +1,2 @@
#define TEST_SUPPRESS_TIME
#include "nextsimtime_cb.c"

View File

@ -0,0 +1 @@
`include "vpi/nextsimtime_cb.v"

View File

@ -0,0 +1,2 @@
#define TEST_NULL_TIME
#include "nextsimtime_cb.c"

View File

@ -0,0 +1 @@
`include "vpi/nextsimtime_cb.v"

View File

@ -47,6 +47,7 @@ static PLI_INT32 test_next_compiletf(PLI_BYTE8 *name)
static PLI_INT32 test_next_calltf(PLI_BYTE8 *name)
{
vpiHandle sys, argv, value;
static s_vpi_time dummy_time = {vpiSimTime, 0, 0, 0};
(void)name; /* Parameter is not used. */
@ -59,6 +60,7 @@ static PLI_INT32 test_next_calltf(PLI_BYTE8 *name)
for (value = vpi_scan(argv) ; value ; value = vpi_scan(argv)) {
s_cb_data cb;
cb.reason = cbNextSimTime;
cb.time = &dummy_time;
cb.cb_rtn = next_sim_time_callback;
cb.user_data = (char*)value;
vpi_register_cb(&cb);

183
ivtest/vpi/sim_time_cb.c Normal file
View File

@ -0,0 +1,183 @@
#ifdef TEST_SCALED_TIME
#define TIME_TYPE vpiScaledRealTime
#else
#define TIME_TYPE vpiSimTime
#endif
#include <vpi_user.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
static PLI_INT32 monitor_cb(p_cb_data cb_data)
{
vpiHandle*var_list = (vpiHandle*)cb_data->user_data;
s_vpi_time time;
s_vpi_value value;
PLI_INT32 index;
time.type = vpiSimTime;
vpi_get_time(NULL, &time);
vpi_printf(" @ %04d :", time.low);
#ifdef TEST_SCALED_TIME
vpi_printf(" cb_data.time = %1.1f :", cb_data->time->real);
#else
vpi_printf(" cb_data.time = %04d :", cb_data->time->low);
#endif
value.format = vpiIntVal;
index = 0;
while (var_list[index]) {
vpi_get_value(var_list[index], &value);
vpi_printf(" %s = %d", vpi_get_str(vpiName, var_list[index]), value.value.integer);
index++;
}
vpi_printf("\n");
return 0;
}
static PLI_INT32 monitor_cb_start(p_cb_data cb_data)
{
vpi_printf("cbStartOfSimTime");
monitor_cb(cb_data);
return 0;
}
static PLI_INT32 monitor_cb_delay(p_cb_data cb_data)
{
vpi_printf("cbAfterDelay ");
monitor_cb(cb_data);
return 0;
}
static PLI_INT32 monitor_cb_synch(p_cb_data cb_data)
{
vpi_printf("cbReadWriteSynch");
monitor_cb(cb_data);
return 0;
}
static PLI_INT32 monitor_cb_end(p_cb_data cb_data)
{
vpiHandle*var_list = (vpiHandle*)cb_data->user_data;
vpi_printf("cbEndOfSimTime ");
monitor_cb(cb_data);
free(var_list);
return 0;
}
static PLI_INT32 monitor_calltf(char*xx)
{
vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, sys);
vpiHandle*var_list = 0;
vpiHandle handle;
PLI_INT32 index;
s_vpi_time time;
s_vpi_time delay;
s_vpi_value value;
s_cb_data cb_data;
(void)xx; /* Parameter is not used. */
delay.type = TIME_TYPE;
delay.low = 0;
delay.high = 0;
delay.real = 0.0;
assert(argv);
handle = vpi_scan(argv);
assert(handle && (vpi_get(vpiType, handle) == vpiConstant));
#ifdef TEST_SCALED_TIME
value.format = vpiRealVal;
vpi_get_value(handle, &value);
delay.real = value.value.real;
#else
value.format = vpiIntVal;
vpi_get_value(handle, &value);
delay.low = value.value.integer;
#endif
index = 0;
while ((handle = vpi_scan(argv))) {
assert(vpi_get(vpiType, handle) == vpiReg);
var_list = realloc(var_list, (index + 1) * sizeof(vpiHandle));
var_list[index++] = handle;
}
var_list = realloc(var_list, (index + 1) * sizeof(vpiHandle));
var_list[index++] = 0;
assert(index > 0);
time.type = TIME_TYPE;
vpi_get_time(var_list[0], &time);
// cbAtStartOfSimTime and cbAtEndOfSimTime want an absolute time.
// We know the test is short, so can ignore the high part.
#ifdef TEST_SCALED_TIME
time.real += delay.real;
#else
time.low += delay.low;
#endif
memset(&cb_data, 0, sizeof(cb_data));
cb_data.reason = cbAtStartOfSimTime;
cb_data.cb_rtn = monitor_cb_start;
cb_data.user_data = (char*)var_list;
cb_data.obj = var_list[0];
cb_data.time = &time;
vpi_register_cb(&cb_data);
cb_data.reason = cbAfterDelay;
cb_data.cb_rtn = monitor_cb_delay;
cb_data.user_data = (char*)var_list;
cb_data.obj = var_list[0];
cb_data.time = &delay;
vpi_register_cb(&cb_data);
// Add cbNBASynch when we support it
cb_data.reason = cbReadWriteSynch;
cb_data.cb_rtn = monitor_cb_synch;
cb_data.user_data = (char*)var_list;
cb_data.obj = var_list[0];
cb_data.time = &delay;
vpi_register_cb(&cb_data);
cb_data.reason = cbAtEndOfSimTime;
cb_data.cb_rtn = monitor_cb_end;
cb_data.user_data = (char*)var_list;
cb_data.obj = var_list[0];
cb_data.time = &time;
vpi_register_cb(&cb_data);
memset(&cb_data, 0, sizeof(cb_data));
memset(&time , 0, sizeof(time));
return 0;
}
static void monitor_register(void)
{
s_vpi_systf_data tf_data;
tf_data.type = vpiSysTask;
tf_data.tfname = "$monitor_time_slot";
tf_data.calltf = monitor_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
vpi_register_systf(&tf_data);
}
void (*vlog_startup_routines[])(void) = {
monitor_register,
0
};

View File

@ -0,0 +1,2 @@
#define TEST_SIM_TIME
#include "sim_time_cb.c"

27
ivtest/vpi/sim_time_cb1.v Normal file
View File

@ -0,0 +1,27 @@
`timescale 1s/1ms
module test;
reg [7:0] a, b;
initial begin
a = 0; b = 0;
$monitor_time_slot(2000, a, b);
$monitor_time_slot(5000, a, b);
#1;
a = 1; b <= 1;
#1;
a = 2; b <= 2;
#1;
a = 3; b <= 3;
#1;
a = 4; b <= 4;
#1;
a = 5; b <= 5;
#1;
a = 6; b <= 6;
#1;
$finish(0);
end
endmodule

View File

@ -0,0 +1,2 @@
#define TEST_SCALED_TIME
#include "sim_time_cb.c"

35
ivtest/vpi/sim_time_cb2.v Normal file
View File

@ -0,0 +1,35 @@
`timescale 1s/1ms
module dut;
reg [7:0] a, b;
endmodule
`timescale 1ms/1ms
module test;
dut dut();
initial begin
dut.a = 0; dut.b = 0;
$monitor_time_slot(2.0, dut.a, dut.b);
$monitor_time_slot(5.0, dut.a, dut.b);
#1000;
dut.a = 1; dut.b <= 1;
#1000;
dut.a = 2; dut.b <= 2;
#1000;
dut.a = 3; dut.b <= 3;
#1000;
dut.a = 4; dut.b <= 4;
#1000;
dut.a = 5; dut.b <= 5;
#1000;
dut.a = 6; dut.b <= 6;
#1000;
$finish(0);
end
endmodule

View File

@ -0,0 +1,137 @@
#if defined(TEST_SCALED_TIME)
#define TIME_TYPE vpiScaledRealTime
#elif defined(TEST_SIM_TIME)
#define TIME_TYPE vpiSimTime
#else
#define TIME_TYPE vpiSuppressTime
#endif
#include <sv_vpi_user.h>
#include <assert.h>
#include <string.h>
static PLI_INT32 report_change(p_cb_data cb)
{
vpiHandle handle = (vpiHandle)(cb->user_data);
s_vpi_time time;
#ifndef TEST_NULL_TIME
assert(cb->time && (cb->time->type == TIME_TYPE));
#endif
assert(cb->value);
#if defined(TEST_SCALED_TIME) || defined(TEST_SIM_TIME)
time = *(cb->time);
#else
time.type = vpiSimTime;
vpi_get_time(NULL, &time);
#endif
switch (cb->value->format) {
case vpiIntVal:
#if defined(TEST_SCALED_TIME)
vpi_printf("At time %f %s = %d\n", time.real, vpi_get_str(vpiName, handle), cb->value->value.integer);
#else
vpi_printf("At time %d %s = %d\n", time.low, vpi_get_str(vpiName, handle), cb->value->value.integer);
#endif
break;
case vpiRealVal:
#ifdef TEST_SCALED_TIME
vpi_printf("At time %f %s = %f\n", time.real, vpi_get_str(vpiName, handle), cb->value->value.real);
#else
vpi_printf("At time %d %s = %f\n", time.low, vpi_get_str(vpiName, handle), cb->value->value.real);
#endif
break;
case vpiSuppressVal:
#ifdef TEST_SCALED_TIME
vpi_printf("At time %f %s changed\n", time.real, vpi_get_str(vpiName, handle));
#else
vpi_printf("At time %d %s changed\n", time.low, vpi_get_str(vpiName, handle));
#endif
break;
default:
vpi_printf("ERROR: unexpected value format %d\n", cb->value->format);
break;
}
return 0;
}
static s_cb_data cb;
static s_vpi_time time;
static s_vpi_value value;
static PLI_INT32 my_monitor_calltf(PLI_BYTE8 *xx)
{
(void)xx; /* Parameter is not used. */
vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, sys);
vpiHandle handle;
memset(&cb, 0, sizeof(cb));
memset(&time, 0, sizeof(time));
memset(&value, 0, sizeof(value));
time.type = TIME_TYPE;
cb.reason = cbValueChange;
cb.cb_rtn = report_change;
cb.time = &time;
cb.value = &value;
while ((handle = vpi_scan(argv))) {
PLI_INT32 type = vpi_get(vpiType, handle);
cb.obj = handle;
cb.user_data = (char *)handle;
switch (type) {
case vpiNet:
case vpiReg:
case vpiIntegerVar:
case vpiBitVar:
case vpiByteVar:
case vpiShortIntVar:
case vpiIntVar:
case vpiLongIntVar:
case vpiPartSelect:
case vpiMemoryWord:
case vpiMemory:
value.format = vpiIntVal;
break;
case vpiRealVar:
value.format = vpiRealVal;
break;
case vpiNamedEvent:
value.format = vpiSuppressVal;
break;
default:
vpi_printf("ERROR: unexpected object type %d\n", type);
break;
}
vpi_register_cb(&cb);
}
memset(&cb, 0, sizeof(cb));
memset(&time, 0, sizeof(time));
memset(&value, 0, sizeof(value));
return 0;
}
static void my_monitor_register(void)
{
s_vpi_systf_data tf_data;
tf_data.type = vpiSysTask;
tf_data.tfname = "$my_monitor";
tf_data.calltf = my_monitor_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
vpi_register_systf(&tf_data);
}
void (*vlog_startup_routines[])(void) = {
my_monitor_register,
0
};

View File

@ -0,0 +1,45 @@
`timescale 1s/1ms
module test;
logic [3:0] v4;
wire [3:0] w4;
integer i4;
bit [3:0] v2;
byte b2;
shortint s2;
int i2;
longint l2;
real r;
event e;
logic [3:0] p4;
logic [3:0] a4[3:0];
bit [3:0] a2[3:0];
assign w4 = v4;
initial begin
$my_monitor(v4, w4, i4, v2, b2, s2, i2, l2, r, e, p4[1:0], a4, a2[1]);
#1 v4 = 4'd1;
#1 i4 = 2;
#1 v2 = 4'd3;
#1 b2 = 4;
#1 s2 = 5;
#1 i2 = 6;
#1 l2 = 7;
#1 r = 8.0;
#1 ->e;
// NOTE: the value change callback on a part select returns the value of the entire variable.
#1 p4 = 4'd10;
#1 a4[0] = 4'd11;
#1 a4[1] = 4'd12;
#1 a2[0] = 4'd13;
#1 a2[1] = 4'd14;
end
endmodule

View File

@ -0,0 +1,2 @@
#define TEST_SIM_TIME
#include "value_change_cb.c"

View File

@ -0,0 +1 @@
`include "vpi/value_change_cb.v"

View File

@ -0,0 +1,2 @@
#define TEST_SCALED_TIME
#include "value_change_cb.c"

View File

@ -0,0 +1 @@
`include "vpi/value_change_cb.v"

View File

@ -0,0 +1,2 @@
#define TEST_SUPPRESS_TIME
#include "value_change_cb.c"

View File

@ -0,0 +1 @@
`include "vpi/value_change_cb.v"

View File

@ -0,0 +1,2 @@
#define TEST_NULL_TIME
#include "value_change_cb.c"

View File

@ -0,0 +1 @@
`include "vpi/value_change_cb.v"

View File

@ -0,0 +1,8 @@
Compiling vpi/nextsimtime_cb1.c...
Making nextsimtime_cb1.vpi from nextsimtime_cb1.o...
time 0: 0
nextsimtime: 1000
time 1: 1
nextsimtime: 4000
time 4: 4
nextsimtime: 5000

View File

@ -0,0 +1,8 @@
Compiling vpi/nextsimtime_cb2.c...
Making nextsimtime_cb2.vpi from nextsimtime_cb2.o...
time 0: 0
nextsimtime: 1.000000
time 1: 1
nextsimtime: 4.000000
time 4: 4
nextsimtime: 5.000000

View File

@ -0,0 +1,8 @@
Compiling vpi/nextsimtime_cb3.c...
Making nextsimtime_cb3.vpi from nextsimtime_cb3.o...
time 0: 0
nextsimtime: 1000
time 1: 1
nextsimtime: 4000
time 4: 4
nextsimtime: 5000

View File

@ -0,0 +1,8 @@
Compiling vpi/nextsimtime_cb4.c...
Making nextsimtime_cb4.vpi from nextsimtime_cb4.o...
time 0: 0
nextsimtime: 1000
time 1: 1
nextsimtime: 4000
time 4: 4
nextsimtime: 5000

View File

@ -0,0 +1,10 @@
Compiling vpi/sim_time_cb1.c...
Making sim_time_cb1.vpi from sim_time_cb1.o...
cbStartOfSimTime @ 2000 : cb_data.time = 2000 : a = 1 b = 1
cbAfterDelay @ 2000 : cb_data.time = 2000 : a = 1 b = 1
cbReadWriteSynch @ 2000 : cb_data.time = 2000 : a = 2 b = 2
cbEndOfSimTime @ 2000 : cb_data.time = 2000 : a = 2 b = 2
cbStartOfSimTime @ 5000 : cb_data.time = 5000 : a = 4 b = 4
cbAfterDelay @ 5000 : cb_data.time = 5000 : a = 4 b = 4
cbReadWriteSynch @ 5000 : cb_data.time = 5000 : a = 5 b = 5
cbEndOfSimTime @ 5000 : cb_data.time = 5000 : a = 5 b = 5

View File

@ -0,0 +1,10 @@
Compiling vpi/sim_time_cb2.c...
Making sim_time_cb2.vpi from sim_time_cb2.o...
cbStartOfSimTime @ 2000 : cb_data.time = 2.0 : a = 1 b = 1
cbAfterDelay @ 2000 : cb_data.time = 2.0 : a = 1 b = 1
cbReadWriteSynch @ 2000 : cb_data.time = 2.0 : a = 2 b = 2
cbEndOfSimTime @ 2000 : cb_data.time = 2.0 : a = 2 b = 2
cbStartOfSimTime @ 5000 : cb_data.time = 5.0 : a = 4 b = 4
cbAfterDelay @ 5000 : cb_data.time = 5.0 : a = 4 b = 4
cbReadWriteSynch @ 5000 : cb_data.time = 5.0 : a = 5 b = 5
cbEndOfSimTime @ 5000 : cb_data.time = 5.0 : a = 5 b = 5

View File

@ -0,0 +1,16 @@
Compiling vpi/value_change_cb1.c...
Making value_change_cb1.vpi from value_change_cb1.o...
At time 1000 v4 = 1
At time 1000 w4 = 1
At time 2000 i4 = 2
At time 3000 v2 = 3
At time 4000 b2 = 4
At time 5000 s2 = 5
At time 6000 i2 = 6
At time 7000 l2 = 7
At time 8000 r = 8.000000
At time 9000 e changed
At time 10000 p4[1:0] = 10
At time 11000 a4 = 11
At time 12000 a4 = 12
At time 14000 a2[1] = 14

View File

@ -0,0 +1,16 @@
Compiling vpi/value_change_cb2.c...
Making value_change_cb2.vpi from value_change_cb2.o...
At time 1.000000 v4 = 1
At time 1.000000 w4 = 1
At time 2.000000 i4 = 2
At time 3.000000 v2 = 3
At time 4.000000 b2 = 4
At time 5.000000 s2 = 5
At time 6.000000 i2 = 6
At time 7.000000 l2 = 7
At time 8.000000 r = 8.000000
At time 9.000000 e changed
At time 10.000000 p4[1:0] = 10
At time 11.000000 a4 = 11
At time 12.000000 a4 = 12
At time 14.000000 a2[1] = 14

View File

@ -0,0 +1,16 @@
Compiling vpi/value_change_cb3.c...
Making value_change_cb3.vpi from value_change_cb3.o...
At time 1000 v4 = 1
At time 1000 w4 = 1
At time 2000 i4 = 2
At time 3000 v2 = 3
At time 4000 b2 = 4
At time 5000 s2 = 5
At time 6000 i2 = 6
At time 7000 l2 = 7
At time 8000 r = 8.000000
At time 9000 e changed
At time 10000 p4[1:0] = 10
At time 11000 a4 = 11
At time 12000 a4 = 12
At time 14000 a2[1] = 14

View File

@ -0,0 +1,16 @@
Compiling vpi/value_change_cb4.c...
Making value_change_cb4.vpi from value_change_cb4.o...
At time 1000 v4 = 1
At time 1000 w4 = 1
At time 2000 i4 = 2
At time 3000 v2 = 3
At time 4000 b2 = 4
At time 5000 s2 = 5
At time 6000 i2 = 6
At time 7000 l2 = 7
At time 8000 r = 8.000000
At time 9000 e changed
At time 10000 p4[1:0] = 10
At time 11000 a4 = 11
At time 12000 a4 = 12
At time 14000 a2[1] = 14

View File

@ -99,6 +99,10 @@ hello_tf normal hello_tf.c hello_tf.log
hello_vpi normal hello_vpi.c hello.log
hello_vpi2 normal hello_vpi2.c hello2.log vpi/hello_vpi1.c
listparams normal listparams.c listparams.log
nextsimtime_cb1 normal nextsimtime_cb1.c nextsimtime_cb1.gold
nextsimtime_cb2 normal nextsimtime_cb2.c nextsimtime_cb2.gold
nextsimtime_cb3 normal nextsimtime_cb3.c nextsimtime_cb3.gold
nextsimtime_cb4 normal nextsimtime_cb4.c nextsimtime_cb4.gold
memmon normal,-g1995 memmon.c memmon.log
memwide normal memwide.cc memwide.log
mipname normal mipname.c mipname.log
@ -128,9 +132,15 @@ scanmem normal scanmem.cc scanmem.log
scanmem2 normal scanmem2.cc scanmem2.log
scanmem3 normal scanmem3.cc scanmem3.log
scopes normal scopes.c scopes.log
sim_time_cb1 normal sim_time_cb1.c sim_time_cb1.gold
sim_time_cb2 normal sim_time_cb2.c sim_time_cb2.gold
spec_delays normal,-gspecify spec_delays.c spec_delays.log
start_of_simtime1 normal start_of_simtime1.c start_of_simtime1.log
timescale normal timescale.c timescale.log
value_change_cb1 normal,-g2009 value_change_cb1.c value_change_cb1.gold
value_change_cb2 normal,-g2009 value_change_cb2.c value_change_cb2.gold
value_change_cb3 normal,-g2009 value_change_cb3.c value_change_cb3.gold
value_change_cb4 normal,-g2009 value_change_cb4.c value_change_cb4.gold
# Add new tests in alphabetic/numeric order. If the test needs
# a compile option or a different log file to run with an older

View File

@ -385,9 +385,7 @@ PLI_INT32 tf_isynchronize(void*obj)
vpiHandle sys = (vpiHandle)obj;
p_pli_data pli = vpi_get_userdata(sys);
s_cb_data cb;
s_vpi_time ti;
ti.type = vpiSuppressTime;
s_vpi_time ti = {vpiSimTime, 0, 0, 0.0};
cb.reason = cbReadWriteSynch;
cb.cb_rtn = callback;
@ -414,7 +412,7 @@ PLI_INT32 tf_irosynchronize(void*obj)
vpiHandle sys = (vpiHandle)obj;
p_pli_data pli = vpi_get_userdata(sys);
s_cb_data cb;
s_vpi_time ti = {vpiSuppressTime, 0, 0, 0.0};
s_vpi_time ti = {vpiSimTime, 0, 0, 0.0};
cb.reason = cbReadOnlySynch;
cb.cb_rtn = callback;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007-2022 Stephen Williams (steve@icarus.com)
* Copyright (c) 2007-2024 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
@ -1319,11 +1319,29 @@ void __vpiArray::word_change(unsigned long addr)
if (addr < vals->get_size())
vals->get_word(addr, val);
vpip_real_get_value(val, cur->cb_data.value);
} else {
} else if (vals4) {
vpip_vec4_get_value(vals4->get_word(addr),
vals_width,
signed_flag,
cur->cb_data.value);
} else if (dynamic_cast<vvp_darray_atom<int8_t>*>(vals)
|| dynamic_cast<vvp_darray_atom<int16_t>*>(vals)
|| dynamic_cast<vvp_darray_atom<int32_t>*>(vals)
|| dynamic_cast<vvp_darray_atom<int64_t>*>(vals)
|| dynamic_cast<vvp_darray_atom<uint8_t>*>(vals)
|| dynamic_cast<vvp_darray_atom<uint16_t>*>(vals)
|| dynamic_cast<vvp_darray_atom<uint32_t>*>(vals)
|| dynamic_cast<vvp_darray_atom<uint64_t>*>(vals)
|| dynamic_cast<vvp_darray_vec2*>(vals)) {
vvp_vector4_t val;
if (addr < vals->get_size())
vals->get_word(addr, val);
vpip_vec4_get_value(val,
vals_width,
signed_flag,
cur->cb_data.value);
} else {
assert(0);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001-2022 Stephen Williams (steve@icarus.com)
* Copyright (c) 2001-2024 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
@ -40,6 +40,119 @@
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
@ -203,6 +316,9 @@ static value_callback*make_value_change_part(p_cb_data data)
*/
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 "
@ -314,13 +430,12 @@ void sync_cb::run_run()
return;
sync_callback*cur = handle;
cur->cb_data.time->type = vpiSimTime;
vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime());
/* 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);
@ -330,8 +445,33 @@ void sync_cb::run_run()
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;
@ -339,46 +479,24 @@ static sync_callback* make_sync(p_cb_data data, bool readonly_flag)
cb->handle = obj;
obj->cb_sync = cb;
vvp_time64_t tv = 0;
switch (obj->cb_time.type) {
case vpiSuppressTime:
break;
case vpiSimTime:
tv = vpip_timestruct_to_time(&obj->cb_time);
break;
default:
fprintf(stderr, "Unsupported time type %d.\n",
(int)obj->cb_time.type);
assert(0);
break;
}
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 = 0;
switch (obj->cb_time.type) {
case vpiSimTime:
tv = vpip_timestruct_to_time(&obj->cb_time);
break;
default:
fprintf(stderr, "Unsupported time type %d.\n",
(int)obj->cb_time.type);
assert(0);
break;
}
vvp_time64_t tv = get_sync_cb_time(obj);
schedule_generic(cb, tv, false);
return obj;
@ -386,25 +504,16 @@ static struct __vpiCallback* make_afterdelay(p_cb_data data)
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 = 0;
switch (obj->cb_time.type) {
case vpiSimTime:
tv = vpip_timestruct_to_time(&obj->cb_time);
break;
default:
fprintf(stderr, "Unsupported time type %d.\n",
(int)obj->cb_time.type);
assert(0);
break;
}
vvp_time64_t tv = get_sync_cb_time(obj);
vvp_time64_t cur = schedule_simtime();
if (cur > tv) {
tv = 0;
@ -421,25 +530,16 @@ static struct __vpiCallback* make_at_start_of_sim_time(p_cb_data data)
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 = 0;
switch (obj->cb_time.type) {
case vpiSimTime:
tv = vpip_timestruct_to_time(&obj->cb_time);
break;
default:
fprintf(stderr, "Unsupported time type %d.\n",
(int)obj->cb_time.type);
assert(0);
break;
}
vvp_time64_t tv = get_sync_cb_time(obj);
vvp_time64_t cur = schedule_simtime();
if (cur > tv) {
tv = 0;
@ -461,12 +561,25 @@ static struct __vpiCallback* make_at_end_of_sim_time(p_cb_data data)
class simulator_callback : public __vpiCallback {
public:
inline explicit simulator_callback(const struct t_cb_data*data)
{ cb_data = *data; }
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;
@ -516,7 +629,7 @@ void vpiEndOfCompile(void) {
cur = EndOfCompile;
EndOfCompile = dynamic_cast<simulator_callback*>(cur->next);
if (cur->cb_data.cb_rtn != 0) {
(cur->cb_data.cb_rtn)(&cur->cb_data);
(cur->cb_data.cb_rtn)(&cur->cb_data);
}
delete cur;
}
@ -538,7 +651,7 @@ void vpiStartOfSim(void) {
cur = StartOfSimulation;
StartOfSimulation = dynamic_cast<simulator_callback*>(cur->next);
if (cur->cb_data.cb_rtn != 0) {
(cur->cb_data.cb_rtn)(&cur->cb_data);
(cur->cb_data.cb_rtn)(&cur->cb_data);
}
delete cur;
}
@ -559,10 +672,8 @@ void vpiPostsim(void) {
cur = EndOfSimulation;
EndOfSimulation = dynamic_cast<simulator_callback*>(cur->next);
if (cur->cb_data.cb_rtn != 0) {
/* Only set the time if it is not NULL. */
if (cur->cb_data.time)
vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime());
(cur->cb_data.cb_rtn)(&cur->cb_data);
set_callback_time(&cur->cb_data);
(cur->cb_data.cb_rtn)(&cur->cb_data);
}
delete cur;
}
@ -577,17 +688,20 @@ void vpiPostsim(void) {
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 (NextSimTime) {
cur = NextSimTime;
NextSimTime = dynamic_cast<simulator_callback*>(cur->next);
if (cur->cb_data.cb_rtn != 0) {
(cur->cb_data.cb_rtn)(&cur->cb_data);
}
delete cur;
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;
@ -595,6 +709,9 @@ void vpiNextSimTime(void)
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 */
@ -614,6 +731,7 @@ static simulator_callback* make_prepost(p_cb_data data)
case cbNextSimTime:
obj->next = NextSimTime;
NextSimTime = obj;
break;
}
return obj;
@ -687,25 +805,7 @@ void callback_execute(struct __vpiCallback*cur)
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(),
static_cast<__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;
}
set_callback_time(&cur->cb_data);
(cur->cb_data.cb_rtn)(&cur->cb_data);
vpi_mode_flag = save_mode;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2023 Stephen Williams (steve@icarus.com)
* Copyright (c) 2008-2024 Stephen Williams (steve@icarus.com)
* Copyright (c) 2023 Leo Moser (leo.moser@pm.me)
*
* This source code is free software; you can redistribute it
@ -495,79 +495,89 @@ char* vpi_get_str(PLI_INT32 property, vpiHandle ref)
return res;
}
int vpip_time_units_from_handle(vpiHandle obj)
__vpiScope*vpip_timescale_scope_from_handle(vpiHandle obj)
{
struct __vpiSysTaskCall*task;
__vpiScope*scope;
struct __vpiSignal*signal;
struct __vpiRealVar*real;
__vpiNamedEvent*event;
if (obj == 0)
return vpip_get_time_precision();
switch (obj->get_type_code()) {
case vpiSysTaskCall:
task = dynamic_cast<__vpiSysTaskCall*>(obj);
return task->scope->time_units;
return task->scope;
case vpiModule:
scope = dynamic_cast<__vpiScope*>(obj);
return scope->time_units;
return dynamic_cast<__vpiScope*>(obj);
case vpiNet:
case vpiReg:
case vpiIntegerVar:
case vpiBitVar:
case vpiByteVar:
case vpiShortIntVar:
case vpiIntVar:
case vpiLongIntVar:
signal = dynamic_cast<__vpiSignal*>(obj);
scope = vpip_scope(signal);
return scope->time_units;
return vpip_scope(signal);
case vpiRealVar:
real = dynamic_cast<__vpiRealVar*>(obj);
return vpip_scope(real);
case vpiNamedEvent:
event = dynamic_cast<__vpiNamedEvent*>(obj);
scope = event->get_scope();
return scope->time_units;
return event->get_scope();
case vpiMemory:
case vpiMemoryWord:
case vpiPartSelect:
return dynamic_cast<__vpiScope*>(obj->vpi_handle(vpiScope));
default:
fprintf(stderr, "ERROR: vpip_time_units_from_handle called with "
fprintf(stderr, "ERROR: vpip_scope_from_handle called with "
"object handle type=%d\n", obj->get_type_code());
assert(0);
return 0;
}
}
int vpip_time_precision_from_handle(vpiHandle obj)
int vpip_time_units_from_handle(vpiHandle obj)
{
struct __vpiSysTaskCall*task;
__vpiScope*scope;
struct __vpiSignal*signal;
if (obj == 0)
return vpip_get_time_precision();
switch (obj->get_type_code()) {
case vpiSysTaskCall:
task = dynamic_cast<__vpiSysTaskCall*>(obj);
return task->scope->time_precision;
__vpiScope*scope = vpip_timescale_scope_from_handle(obj);
if (scope == 0)
return vpip_get_time_precision();
case vpiModule:
scope = dynamic_cast<__vpiScope*>(obj);
return scope->time_precision;
return scope->time_units;
}
case vpiNet:
case vpiReg:
signal = dynamic_cast<__vpiSignal*>(obj);
scope = vpip_scope(signal);
return scope->time_precision;
int vpip_time_precision_from_handle(vpiHandle obj)
{
if (obj == 0)
return vpip_get_time_precision();
default:
fprintf(stderr, "ERROR: vpip_time_precision_from_handle called "
"with object handle type=%d\n", obj->get_type_code());
assert(0);
return 0;
}
__vpiScope*scope = vpip_timescale_scope_from_handle(obj);
if (scope == 0)
return vpip_get_time_precision();
return scope->time_precision;
}
double vpip_scaled_time_from_handle(vvp_time64_t time, vpiHandle obj)
{
int scale = vpip_get_time_precision() -
vpip_time_units_from_handle(obj);
if (scale >= 0)
return (double)time * pow(10.0, scale);
else
return (double)time / pow(10.0, -scale);
}
void vpi_get_time(vpiHandle obj, s_vpi_time*vp)
{
int scale;
vvp_time64_t time;
assert(vp);
@ -581,10 +591,7 @@ void vpi_get_time(vpiHandle obj, s_vpi_time*vp)
break;
case vpiScaledRealTime:
scale = vpip_get_time_precision() -
vpip_time_units_from_handle(obj);
if (scale >= 0) vp->real = (double)time * pow(10.0, scale);
else vp->real = (double)time / pow(10.0, -scale);
vp->real = vpip_scaled_time_from_handle(time, obj);
break;
default:

View File

@ -1076,9 +1076,13 @@ vpiHandle vpip_sim_realtime(__vpiScope*scope);
extern int vpip_get_time_precision(void);
extern void vpip_set_time_precision(int pres);
extern __vpiScope*vpip_timescale_scope_from_handle(vpiHandle obj);
extern int vpip_time_units_from_handle(vpiHandle obj);
extern int vpip_time_precision_from_handle(vpiHandle obj);
extern double vpip_scaled_time_from_handle(vvp_time64_t time, vpiHandle obj);
extern void vpip_time_to_timestruct(struct t_vpi_time*ts, vvp_time64_t ti);
extern vvp_time64_t vpip_timestruct_to_time(const struct t_vpi_time*ts);