diff --git a/Documentation/usage/command_line_flags.rst b/Documentation/usage/command_line_flags.rst index 302021957..a3d9abfbf 100644 --- a/Documentation/usage/command_line_flags.rst +++ b/Documentation/usage/command_line_flags.rst @@ -342,9 +342,9 @@ These flags affect the general behavior of the compiler. * select-range - This enables warnings for constant out-of-bound selects. This includes - partial or fully out-of-bound select as well as a select containing a 'bx - or 'bz in the index. + This enables warnings for constant and runtime out-of-bound selects. This + includes partial or fully out-of-bound select as well as a select containing + a 'bx or 'bz in the index. * timescale diff --git a/driver/iverilog.man.in b/driver/iverilog.man.in index b5f4d1360..259023ab2 100644 --- a/driver/iverilog.man.in +++ b/driver/iverilog.man.in @@ -421,8 +421,8 @@ will generate a warning. .TP 8 .B select-range -This enables warnings for constant out of bound selects. This includes -partial or fully out of bound selects as well as a select containing +This enables warnings for constant and runtime out-of-bound selects. This +includes partial or fully out-of-bound select as well as a select containing a 'bx or 'bz in the index. .TP 8 diff --git a/ivtest/gold/unpacked_array_oob_write_nowarn-vvp-stdout.gold b/ivtest/gold/unpacked_array_oob_write_nowarn-vvp-stdout.gold new file mode 100644 index 000000000..53cdf1e93 --- /dev/null +++ b/ivtest/gold/unpacked_array_oob_write_nowarn-vvp-stdout.gold @@ -0,0 +1 @@ +PASSED diff --git a/ivtest/gold/unpacked_array_oob_write_warn-vvp-stderr.gold b/ivtest/gold/unpacked_array_oob_write_warn-vvp-stderr.gold new file mode 100644 index 000000000..2fac554ca --- /dev/null +++ b/ivtest/gold/unpacked_array_oob_write_warn-vvp-stderr.gold @@ -0,0 +1,7 @@ +ivltests/unpacked_array_oob_write_warn.v:21: Warning: out-of-bounds unpacked array write index 4 for 'a[0:3]'; write ignored. +ivltests/unpacked_array_oob_write_warn.v:24: Warning: out-of-bounds unpacked array write index -1 for 'a[0:3]'; write ignored. +ivltests/unpacked_array_oob_write_warn.v:27: Warning: out-of-bounds unpacked array write index 5 for 'a[0:3]'; write ignored. +ivltests/unpacked_array_oob_write_warn.v:30: Warning: out-of-bounds unpacked array write index 3 for 'r[0:1]'; write ignored. +ivltests/unpacked_array_oob_write_warn.v:33: Warning: undefined unpacked array write index for 'a[0:3]'; write ignored. +ivltests/unpacked_array_oob_write_warn.v:36: Warning: undefined unpacked array write index for 'a[0:3]'; write ignored. +ivltests/unpacked_array_oob_write_warn.v:39: Warning: undefined unpacked array write index for 'r[0:1]'; write ignored. diff --git a/ivtest/gold/unpacked_array_oob_write_warn-vvp-stdout.gold b/ivtest/gold/unpacked_array_oob_write_warn-vvp-stdout.gold new file mode 100644 index 000000000..53cdf1e93 --- /dev/null +++ b/ivtest/gold/unpacked_array_oob_write_warn-vvp-stdout.gold @@ -0,0 +1 @@ +PASSED diff --git a/ivtest/ivltests/unpacked_array_oob_write_warn.v b/ivtest/ivltests/unpacked_array_oob_write_warn.v new file mode 100644 index 000000000..babc99bf4 --- /dev/null +++ b/ivtest/ivltests/unpacked_array_oob_write_warn.v @@ -0,0 +1,57 @@ +module main; + logic [7:0] a [0:3]; + real r [0:1]; + integer idx; + integer errors; + + initial begin + errors = 0; + + a[0] = 8'h11; + a[1] = 8'h22; + a[2] = 8'h33; + a[3] = 8'h44; + r[0] = 1.25; + r[1] = 2.50; + + idx = 2; + a[idx] = 8'haa; + + idx = 4; + a[idx] = 8'hff; + + idx = -1; + a[idx] <= 8'hee; + + idx = 5; + repeat (2) a[idx] = 8'h77; + + idx = 3; + r[idx] <= 9.50; + + idx = 'bx; + a[idx] = 8'h66; + + idx = 'bz; + a[idx] <= 8'h55; + + idx = 'bx; + r[idx] = 7.25; + + #1; + + if (a[0] !== 8'h11) errors = errors + 1; + if (a[1] !== 8'h22) errors = errors + 1; + if (a[2] !== 8'haa) errors = errors + 1; + if (a[3] !== 8'h44) errors = errors + 1; + if (r[0] != 1.25) errors = errors + 1; + if (r[1] != 2.50) errors = errors + 1; + + if (errors) begin + $display("FAILED"); + $finish(1); + end + + $display("PASSED"); + end +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 2475c4d3a..c3ef00a1d 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -380,6 +380,8 @@ test_va_math vvp_tests/test_va_math.json test_vams_math vvp_tests/test_vams_math.json timing_check_syntax vvp_tests/timing_check_syntax.json timing_check_delayed_signals vvp_tests/timing_check_delayed_signals.json +unpacked_array_oob_write_nowarn vvp_tests/unpacked_array_oob_write_nowarn.json +unpacked_array_oob_write_warn vvp_tests/unpacked_array_oob_write_warn.json uwire_fail2 vvp_tests/uwire_fail2.json uwire_fail3 vvp_tests/uwire_fail3.json value_range1 vvp_tests/value_range1.json diff --git a/ivtest/vvp_tests/unpacked_array_oob_write_nowarn.json b/ivtest/vvp_tests/unpacked_array_oob_write_nowarn.json new file mode 100644 index 000000000..528a0b0b2 --- /dev/null +++ b/ivtest/vvp_tests/unpacked_array_oob_write_nowarn.json @@ -0,0 +1,6 @@ +{ + "type" : "normal", + "source" : "unpacked_array_oob_write_warn.v", + "iverilog-args" : [ "-g2005-sv", "-pfileline=1" ], + "gold" : "unpacked_array_oob_write_nowarn" +} diff --git a/ivtest/vvp_tests/unpacked_array_oob_write_warn.json b/ivtest/vvp_tests/unpacked_array_oob_write_warn.json new file mode 100644 index 000000000..833b78fbc --- /dev/null +++ b/ivtest/vvp_tests/unpacked_array_oob_write_warn.json @@ -0,0 +1,6 @@ +{ + "type" : "normal", + "source" : "unpacked_array_oob_write_warn.v", + "iverilog-args" : [ "-g2005-sv", "-Wselect-range", "-pfileline=1" ], + "gold" : "unpacked_array_oob_write_warn" +} diff --git a/main.cc b/main.cc index 917e006e9..0e253dc98 100644 --- a/main.cc +++ b/main.cc @@ -749,6 +749,8 @@ static void read_iconfig_file(const char*ipath) roots.push_back(lex_strings.make(cp)); } else if (strcmp(buf,"warnings") == 0) { + free(reinterpret_cast(const_cast(flags["warnings"]))); + flags["warnings"] = strdup(cp); /* Scan the warnings enable string for warning flags. */ for ( ; *cp ; cp += 1) switch (*cp) { case 'f': diff --git a/tgt-vvp/vvp.c b/tgt-vvp/vvp.c index 074e8d64d..f275ef118 100644 --- a/tgt-vvp/vvp.c +++ b/tgt-vvp/vvp.c @@ -162,6 +162,7 @@ int target_design(ivl_design_t des) * printed for procedural statements. (e.g. -pfileline=1). * The default is no file/line information will be included. */ const char*fileline = ivl_design_flag(des, "fileline"); + const char*warnings = ivl_design_flag(des, "warnings"); const char*debug_flags = ivl_design_flag(des, "debug_flags"); process_debug_string(debug_flags); @@ -210,6 +211,8 @@ int target_design(ivl_design_t des) fprintf(vvp_out, ":ivl_delay_selection \"%s\";\n", ivl_design_delay_sel(des)); + if (warnings && *warnings) + fprintf(vvp_out, ":ivl_warnings \"%s\";\n", warnings); { int pre = ivl_design_time_precision(des); char sign = '+'; diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index 8382b94ab..a1bfdd9af 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -40,7 +40,6 @@ static void assign_to_array_r_word(ivl_signal_t lsig, ivl_expr_t word_ix, uint64_t delay, ivl_expr_t dexp, unsigned nevents) { - unsigned end_assign = transient_id++; int word_ix_reg = 3; /* if we have to evaluate a delay expression, evaluate word index into @@ -54,24 +53,23 @@ static void assign_to_array_r_word(ivl_signal_t lsig, ivl_expr_t word_ix, !number_is_unknown(word_ix)) { fprintf(vvp_out, " %%ix/load %d, %ld, 0; address\n", word_ix_reg, get_number_immediate(word_ix)); + fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); } else { /* Calculate array word index into index register 3 */ draw_eval_expr_into_integer(word_ix, word_ix_reg); - /* Skip assignment if word expression is not defined. */ - unsigned do_assign = transient_id++; - fprintf(vvp_out, " %%jmp/0 t_%u, 4;\n", do_assign); - fprintf(vvp_out, " %%pop/real 1;\n"); - fprintf(vvp_out, " %%jmp t_%u;\n", end_assign); - fprintf(vvp_out, "t_%u ;\n", do_assign); } if (dexp != 0) { /* Calculated delay... */ int delay_index = allocate_word(); + int index_flag = allocate_flag(); + fprintf(vvp_out, " %%flag_mov %d, 4;\n", index_flag); draw_eval_expr_into_integer(dexp, delay_index); fprintf(vvp_out, " %%ix/mov 3, %d;\n", word_ix_reg); + fprintf(vvp_out, " %%flag_mov 4, %d;\n", index_flag); fprintf(vvp_out, " %%assign/ar/d v%p, %d;\n", lsig, delay_index); + clr_flag(index_flag); clr_word(word_ix_reg); clr_word(delay_index); } else if (nevents != 0) { @@ -99,7 +97,6 @@ static void assign_to_array_r_word(ivl_signal_t lsig, ivl_expr_t word_ix, } } - fprintf(vvp_out, "t_%u ;\n", end_assign); if (nevents != 0) fprintf(vvp_out, " %%evctl/c;\n"); } diff --git a/vvp/compile.h b/vvp/compile.h index cccc2391a..e98f37ed5 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -47,6 +47,7 @@ extern void compile_init(void); extern void compile_cleanup(void); extern bool verbose_flag; +extern bool warn_array_write_oob; /* * If this file opened, then write debug information to this diff --git a/vvp/lexor.lex b/vvp/lexor.lex index 1a6ce88ef..cb45fdd88 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -77,6 +77,7 @@ inline uint64_t strtouint64(const char*str, char**endptr, int base) /* These are some special header/footer keywords. */ ^":ivl_version" { return K_ivl_version; } ^":ivl_delay_selection" { return K_ivl_delay_selection; } +^":ivl_warnings" { return K_ivl_warnings; } ^":vpi_module" { return K_vpi_module; } ^":vpi_time_precision" { return K_vpi_time_precision; } ^":file_names" { return K_file_names; } diff --git a/vvp/lib_main.cc b/vvp/lib_main.cc index 3e1832b6e..398eaf1c7 100644 --- a/vvp/lib_main.cc +++ b/vvp/lib_main.cc @@ -53,6 +53,7 @@ extern "C" const char*optarg; #endif bool verbose_flag = false; +bool warn_array_write_oob = false; static int vvp_return_value = 0; static int vvp_used = 0; @@ -214,6 +215,12 @@ void set_delay_selection(const char* sel) delete[] sel; } +void set_warning_flags(const char* sel) +{ + warn_array_write_oob = strchr(sel, 's') != 0; + delete[] sel; +} + static void final_cleanup() { vvp_object::cleanup(); diff --git a/vvp/parse.y b/vvp/parse.y index bd8dba1bf..ed2a94335 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -105,7 +105,7 @@ static struct __vpiModPath*modpath_dst = 0; %token K_VAR_S K_VAR_STR K_VAR_I K_VAR_R K_VAR_2S K_VAR_2U %token K_vpi_call K_vpi_call_w K_vpi_call_i %token K_vpi_func K_vpi_func_r K_vpi_func_s -%token K_ivl_version K_ivl_delay_selection +%token K_ivl_version K_ivl_delay_selection K_ivl_warnings %token K_vpi_module K_vpi_time_precision K_file_names K_file_line %token K_PORT_INPUT K_PORT_OUTPUT K_PORT_INOUT K_PORT_MIXED K_PORT_NODIR @@ -153,6 +153,8 @@ header_line { verify_version($2, $3); } | K_ivl_delay_selection T_STRING ';' { set_delay_selection($2); } + | K_ivl_warnings T_STRING ';' + { set_warning_flags($2); } | K_vpi_module T_STRING ';' { compile_load_vpi_module($2); } | K_vpi_time_precision '+' T_NUMBER ';' diff --git a/vvp/parse_misc.h b/vvp/parse_misc.h index a63cfd11f..c36aafdcc 100644 --- a/vvp/parse_misc.h +++ b/vvp/parse_misc.h @@ -38,6 +38,11 @@ extern void verify_version(char *ivl_ver, char* commit); */ extern void set_delay_selection(const char* sel); +/* + * Set runtime warning classes embedded in the VVP input file. + */ +extern void set_warning_flags(const char* sel); + /* * various functions shared by the lexor and the parser. */ diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 98cd41a99..dd5a7db4b 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -20,6 +20,7 @@ # include "config.h" # include "vthread.h" # include "codes.h" +# include "compile.h" # include "schedule.h" # include "ufunc.h" # include "event.h" @@ -1076,6 +1077,42 @@ bool of_ADD_WR(vthread_t thr, vvp_code_t) return true; } +static set warned_array_write_oob; + +static void maybe_warn_array_write_oob(vthread_t thr, vvp_code_t cp, + int64_t adr) +{ + if (!warn_array_write_oob) + return; + + assert(cp->array); + + bool index_undef = thr->flags[4] == BIT4_1; + if (!index_undef && adr >= 0 && (uint64_t) adr < cp->array->get_size()) + return; + + if (!warned_array_write_oob.insert(cp).second) + return; + + int left = cp->array->swap_addr ? cp->array->last_addr.get_value() + : cp->array->first_addr.get_value(); + int right = cp->array->swap_addr ? cp->array->first_addr.get_value() + : cp->array->last_addr.get_value(); + + cerr << thr->get_fileline(); + if (index_undef) { + cerr << "Warning: undefined unpacked array write index for '"; + } else { + int64_t source_index = adr + + (int64_t) cp->array->first_addr.get_value(); + cerr << "Warning: out-of-bounds unpacked array write index " + << source_index << " for '"; + } + + cerr << cp->array->name << "[" << left << ":" << right + << "]'; write ignored." << endl; +} + /* %assign/ar , * Generate an assignment event to a real array. Index register 3 * contains the canonical address of the word in the memory. @@ -1088,6 +1125,11 @@ bool of_ASSIGN_AR(vthread_t thr, vvp_code_t cp) unsigned delay = cp->bit_idx[0]; double value = thr->pop_real(); + maybe_warn_array_write_oob(thr, cp, adr); + + if (thr->flags[4] == BIT4_1) + return true; + if (adr >= 0) { schedule_assign_array_word(cp->array, adr, value, delay); } @@ -1105,6 +1147,11 @@ bool of_ASSIGN_ARD(vthread_t thr, vvp_code_t cp) long adr = thr->words[3].w_int; double value = thr->pop_real(); + maybe_warn_array_write_oob(thr, cp, adr); + + if (thr->flags[4] == BIT4_1) + return true; + if (adr >= 0) { vvp_time64_t delay = thr->words[cp->bit_idx[0]].w_uint; schedule_assign_array_word(cp->array, adr, value, delay); @@ -1125,6 +1172,11 @@ bool of_ASSIGN_ARE(vthread_t thr, vvp_code_t cp) long adr = thr->words[3].w_int; double value = thr->pop_real(); + maybe_warn_array_write_oob(thr, cp, adr); + + if (thr->flags[4] == BIT4_1) + return true; + if (adr >= 0) { if (thr->ecount == 0) { schedule_assign_array_word(cp->array, adr, value, 0); @@ -1210,6 +1262,8 @@ bool of_ASSIGN_VEC4_A_D(vthread_t thr, vvp_code_t cp) vvp_vector4_t val = thr->pop_vec4(); + maybe_warn_array_write_oob(thr, cp, adr); + // Abort if flags[4] is set. This can happen if the calculation // into an index register failed. if (thr->flags[4] == BIT4_1) @@ -1236,6 +1290,8 @@ bool of_ASSIGN_VEC4_A_E(vthread_t thr, vvp_code_t cp) vvp_vector4_t val = thr->pop_vec4(); + maybe_warn_array_write_oob(thr, cp, adr); + // Abort if flags[4] is set. This can happen if the calculation // into an index register failed. if (thr->flags[4] == BIT4_1) @@ -5770,6 +5826,11 @@ bool of_STORE_OBJA(vthread_t thr, vvp_code_t cp) vvp_object_t val; thr->pop_object(val); + maybe_warn_array_write_oob(thr, cp, adr); + + if (thr->flags[4] == BIT4_1) + return true; + cp->array->set_word(adr, val); return true; @@ -6089,8 +6150,10 @@ static bool storea(vthread_t thr, vvp_code_t cp) ELEM val; pop_value(thr, val, 0); + unsigned adr = thr->words[idx].w_int; + maybe_warn_array_write_oob(thr, cp, adr); + if (thr->flags[4] != BIT4_1) { - unsigned adr = thr->words[idx].w_int; cp->array->set_word(adr, val); } @@ -6191,6 +6254,8 @@ bool of_STORE_VEC4A(vthread_t thr, vvp_code_t cp) long adr = adr_index? thr->words[adr_index].w_int : 0; int64_t off = off_index ? thr->words[off_index].w_int : 0; + maybe_warn_array_write_oob(thr, cp, adr); + // Suppress action if flags-4 is true. if (thr->flags[4] == BIT4_1) { thr->pop_vec4(1);