diff --git a/ivtest/vpi/br_gh1041.c b/ivtest/vpi/br_gh1041.c new file mode 100644 index 0000000000..f057a82362 --- /dev/null +++ b/ivtest/vpi/br_gh1041.c @@ -0,0 +1,52 @@ +#include + +static PLI_INT32 start_cb(struct t_cb_data *cb) +{ + static struct t_vpi_value val; + vpiHandle wire; + + (void)cb; // suppress unused parameter warning + + wire = vpi_handle_by_name("test.w4", NULL); + if (wire) { + val.format = vpiIntVal; + val.value.integer = 1; + vpi_put_value(wire, &val, NULL, vpiNoDelay); + } else { + vpi_printf("Failed to get handle for w4\n"); + } + + wire = vpi_handle_by_name("test.w8", NULL); + if (wire) { + val.format = vpiIntVal; + val.value.integer = 1; + vpi_put_value(wire, &val, NULL, vpiNoDelay); + } else { + vpi_printf("Failed to get handle for w8\n"); + } + + wire = vpi_handle_by_name("test.wr", NULL); + if (wire) { + val.format = vpiRealVal; + val.value.real = 1.0; + vpi_put_value(wire, &val, NULL, vpiNoDelay); + } else { + vpi_printf("Failed to get handle for wr\n"); + } + + return 0; +} + +static void register_cb(void) +{ + struct t_cb_data cbd = {}; + + cbd.reason = cbStartOfSimulation; + cbd.cb_rtn = start_cb; + vpi_register_cb(&cbd); +} + +void (*vlog_startup_routines[])(void) = { + register_cb, + 0 +}; diff --git a/ivtest/vpi/br_gh1041.v b/ivtest/vpi/br_gh1041.v new file mode 100644 index 0000000000..bb06ac52ef --- /dev/null +++ b/ivtest/vpi/br_gh1041.v @@ -0,0 +1,20 @@ +module test; + wire w4; + tri0 w8; + wire real wr; + + reg failed = 0; + + initial begin + #0; + $display("w4 %b w8 %b wr %f", w4, w8, wr); + if (w4 !== 1'b1) failed = 1; + if (w8 !== 1'b1) failed = 1; + if (wr != 1.0) failed = 1; + + if (failed) + $display("FAILED"); + else + $display("PASSED"); + end +endmodule diff --git a/ivtest/vpi/br_gh1041b.c b/ivtest/vpi/br_gh1041b.c new file mode 100644 index 0000000000..b7d5947a62 --- /dev/null +++ b/ivtest/vpi/br_gh1041b.c @@ -0,0 +1,74 @@ +#include + +static void step(void); + +static vpiHandle w; + +static PLI_INT32 tick_cb(struct t_cb_data *cb) +{ + static struct t_vpi_value val = { .format = vpiIntVal }; + static int idx; + + (void)cb; + + ++idx; + val.value.integer = idx & 1; + vpi_put_value(w, &val, NULL, vpiNoDelay); + step(); + return 0; +} + +/* Request a callback after a delay. */ + +static void step(void) +{ + static struct t_vpi_time now = { .type = vpiSimTime, .low = 2 }; + static struct t_cb_data cbd = + { .reason = cbAfterDelay, .cb_rtn = tick_cb, .time = &now }; + + /* Callback after delay. */ + + vpi_register_cb(&cbd); +} + +/* Callback function - simulation is starting. */ + +static PLI_INT32 start_cb(struct t_cb_data *cb) +{ + static struct t_vpi_value val = { .format = vpiIntVal }; + + (void)cb; + + w = vpi_handle_by_name("test.w", NULL); + if (!w) + vpi_printf("No handle!\n"); + vpi_printf("Got handle for %s\n", vpi_get_str(vpiFullName, w)); + val.value.integer = 0; + vpi_put_value(w, &val, NULL, vpiNoDelay); + step(); + return 0; +} + +/* VPI initialisation. */ + +static void start(void) +{ + static struct t_vpi_time now = { .type = vpiSimTime }; + static struct t_cb_data cbd = { .reason = cbStartOfSimulation, + .time = &now, .cb_rtn = start_cb }; + + /* At this point VPI objects do not exist, + * so request a callback once they do. + */ + + vpi_register_cb(&cbd); +} + +/* This is a table of registration functions. This table is the external + * symbol that the VVP simulator looks for when loading this .vpi module. + */ + +void (*vlog_startup_routines[])(void) = { + start, + 0 +}; diff --git a/ivtest/vpi/br_gh1041b.v b/ivtest/vpi/br_gh1041b.v new file mode 100644 index 0000000000..c4a6acc719 --- /dev/null +++ b/ivtest/vpi/br_gh1041b.v @@ -0,0 +1,18 @@ +module test(w); + input wire w; + wire a, b; + + + initial begin + #11 $finish; + end + + assign b = 0; + + assign a = !w | b; + + always @(a) begin + $display($time, ": Wire a is now ", a); + end +endmodule + diff --git a/ivtest/vpi_gold/br_gh1041.gold b/ivtest/vpi_gold/br_gh1041.gold new file mode 100644 index 0000000000..9365276e6a --- /dev/null +++ b/ivtest/vpi_gold/br_gh1041.gold @@ -0,0 +1,4 @@ +Compiling vpi/br_gh1041.c... +Making br_gh1041.vpi from br_gh1041.o... +w4 1 w8 1 wr 1.000000 +PASSED diff --git a/ivtest/vpi_gold/br_gh1041b.gold b/ivtest/vpi_gold/br_gh1041b.gold new file mode 100644 index 0000000000..d8c1193f67 --- /dev/null +++ b/ivtest/vpi_gold/br_gh1041b.gold @@ -0,0 +1,10 @@ +Compiling vpi/br_gh1041b.c... +Making br_gh1041b.vpi from br_gh1041b.o... +Got handle for test.w + 0: Wire a is now 1 + 2: Wire a is now 0 + 4: Wire a is now 1 + 6: Wire a is now 0 + 8: Wire a is now 1 + 10: Wire a is now 0 +vpi/br_gh1041b.v:7: $finish called at 11 (1s) diff --git a/ivtest/vpi_regress.list b/ivtest/vpi_regress.list index 240261b84f..2260800817 100644 --- a/ivtest/vpi_regress.list +++ b/ivtest/vpi_regress.list @@ -71,6 +71,8 @@ br_gh308 normal br_gh308.c br_gh308.gold br_gh317 normal br_gh317.c br_gh317.gold br_gh496 normal,-g2009 br_gh496.c br_gh496.gold br_gh1037 normal,-g2009 br_gh1037.c br_gh1037.gold +br_gh1041 normal br_gh1041.c br_gh1041.gold +br_gh1041b normal br_gh1041b.c br_gh1041b.gold br_ml20191013 normal br_ml20191013.c br_ml20191013.gold by_index normal by_index.c by_index.gold by_name normal by_name.c by_name.log diff --git a/tgt-vvp/draw_net_input.c b/tgt-vvp/draw_net_input.c index 53f94f9083..2a078bb714 100644 --- a/tgt-vvp/draw_net_input.c +++ b/tgt-vvp/draw_net_input.c @@ -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 @@ -651,18 +651,20 @@ static void draw_net_input_x(ivl_nexus_t nex, nex_data->flags |= nex_flags; /* If the nexus has no drivers, then send a constant HiZ or - 0.0 into the net. */ + 0.0 into the net. Use a lower case 'c' prefix for the + constant to inform vvp that this is an undriven value. */ if (ndrivers == 0) { unsigned wid = width_of_nexus(nex); int pull = (res == IVL_SIT_TRI0) || (res == IVL_SIT_TRI1); /* For real nets put 0.0. */ if (signal_data_type_of_nexus(nex) == IVL_VT_REAL) { nex_private = draw_Cr_to_string(0.0); + nex_private[0] = 'c'; } else { unsigned jdx; char*tmp = malloc((pull ? 3 : 1) * wid + 5); nex_private = tmp; - strcpy(tmp, pull ? "C8<" : "C4<"); + strcpy(tmp, pull ? "c8<" : "c4<"); tmp += strlen(tmp); switch (res) { case IVL_SIT_TRI: diff --git a/vvp/compile.cc b/vvp/compile.cc index 46ec619d0d..c56e44a694 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2021 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 @@ -891,9 +891,20 @@ void compile_vpi_time_precision(long pre) * * The real value is sign * (mant ** exp). */ +static bool crstring_header_test(const char*str) +{ + if ((str[0] != 'C') && (str[0] != 'c')) + return false; + if ((str[1] != 'r') || (str[2] != '<')) + return false; + + return true; +} + bool crstring_test(const char*str) { - if (strncmp(str, "Cr<", 3) != 0) return false; + if (!crstring_header_test(str)) + return false; const char*tp = strchr(str, '>'); if (tp == 0) return false; if (tp[1] != 0) return false; @@ -906,6 +917,8 @@ bool crstring_test(const char*str) double crstring_to_double(const char*label) { + assert(crstring_header_test(label)); + const char*cp = label+3; assert(*cp == 'm'); cp += 1; @@ -956,14 +969,21 @@ void input_connect(vvp_net_t*fdx, unsigned port, char*label) vvp_vector4_t tmp = c4string_to_vector4(label); - // Inputs that are constants are schedule to execute as + // Inputs that are constants are scheduled to execute as // soon at the simulation starts. In Verilog, constants // start propagating when the simulation starts, just // like any other signal value. But letting the // scheduler distribute the constant value has the // additional advantage that the constant is not // propagated until the network is fully linked. - schedule_set_vector(ifdx, tmp); + // For constants that initialise an undriven net, we + // schedule execution before time 0, to make sure it + // occurs before any sensitive processes are started + // or VPI callbacks are executed. + if (label[0] == 'c') + schedule_init_vector(ifdx, tmp); + else + schedule_set_vector(ifdx, tmp); free(label); return; @@ -973,7 +993,10 @@ void input_connect(vvp_net_t*fdx, unsigned port, char*label) if (c8string_test(label)) { vvp_vector8_t tmp = c8string_to_vector8(label); - schedule_set_vector(ifdx, tmp); + if (label[0] == 'c') + schedule_init_vector(ifdx, tmp); + else + schedule_set_vector(ifdx, tmp); free(label); return; @@ -985,7 +1008,10 @@ void input_connect(vvp_net_t*fdx, unsigned port, char*label) double tmp = crstring_to_double(label); - schedule_set_vector(ifdx, tmp); + if (label[0] == 'c') + schedule_init_vector(ifdx, tmp); + else + schedule_set_vector(ifdx, tmp); free(label); return; } diff --git a/vvp/lexor.lex b/vvp/lexor.lex index 94318db35e..1b59cc415d 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -4,7 +4,7 @@ %{ /* - * Copyright (c) 2001-2018 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 @@ -263,17 +263,17 @@ static char* strdupnew(char const *str) /* Handle some specialized constant/literals as symbols. */ -"C4<"[01xz]*">" { +[Cc]"4<"[01xz]*">" { yylval.text = strdup(yytext); assert(yylval.text); return T_SYMBOL; } -"C8<"[01234567xz]*">" { +[Cc]"8<"[01234567xz]*">" { yylval.text = strdup(yytext); assert(yylval.text); return T_SYMBOL; } -"Cr" { +[Cc]"r" { yylval.text = strdup(yytext); assert(yylval.text); return T_SYMBOL; } diff --git a/vvp/schedule.cc b/vvp/schedule.cc index 961aa36e65..c9d117ef56 100644 --- a/vvp/schedule.cc +++ b/vvp/schedule.cc @@ -1147,6 +1147,8 @@ void schedule_simulate(void) delete cur; } + sim_started = true; + if (verbose_flag) { vpi_mcd_printf(1, " ...execute StartOfSim callbacks\n"); } @@ -1154,8 +1156,6 @@ void schedule_simulate(void) // Execute start of simulation callbacks vpiStartOfSim(); - sim_started = true; - signals_capture(); if (verbose_flag) { diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index ef995d57ed..ebe223ce0f 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2023 Stephen Williams (steve@icarus.com) + * Copyright (c) 2004-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 @@ -2999,9 +2999,18 @@ vvp_vector4_t vector2_to_vector4(const vvp_vector2_t&that, unsigned wid) return res; } +static bool c4string_header_test(const char*str) +{ + if ((str[0] != 'C') && (str[0] != 'c')) + return false; + if ((str[1] != '4') || (str[2] != '<')) + return false; + return true; +} + bool c4string_test(const char*str) { - if (strncmp(str, "C4<", 3) != 0) + if (!c4string_header_test(str)) return false; size_t value_size = strspn(str+3, "01xz"); if (str[3+value_size] != '>') @@ -3014,7 +3023,7 @@ bool c4string_test(const char*str) vvp_vector4_t c4string_to_vector4(const char*str) { - assert((str[0]=='C') && (str[1]=='4') && (str[2]=='<')); + assert(c4string_header_test(str)); str += 3; const char*tp = str + strspn(str,"01xz"); @@ -3186,14 +3195,21 @@ vvp_vector8_t part_expand(const vvp_vector8_t&that, unsigned wid, unsigned off) return tmp; } +static bool c8string_header_test(const char*str) +{ + if ((str[0] != 'C') && (str[0] != 'c')) + return false; + if ((str[1] != '8') || (str[2] != '<')) + return false; + return true; +} + bool c8string_test(const char*str) { - const char*cp; - if (str[0] != 'C') return false; - if (str[1] != '8') return false; - if (str[2] != '<') return false; + if (!c8string_header_test(str)) + return false; - cp = str+3; + const char*cp = str+3; for (;; cp += 1) { if (cp[0] == '>' && cp[1] == 0) return true; if (cp[0] >= '0' && cp[0] <= '9') continue; @@ -3210,6 +3226,8 @@ bool c8string_test(const char*str) */ vvp_vector8_t c8string_to_vector8(const char*str) { + assert(c8string_header_test(str)); + size_t vsize = strlen(str)-4; assert(vsize%3 == 0); vsize /= 3;