diff --git a/PExpr.h b/PExpr.h index f08086e9e..6331ddd3a 100644 --- a/PExpr.h +++ b/PExpr.h @@ -154,11 +154,12 @@ class PExpr : public LineInfo { // Expressions that can be in the l-value of procedural // assignments can be elaborated with this method. If the - // is_force flag is true, then the set of valid l-value types - // is slightly modified to accommodate the Verilog force - // statement + // is_cassign or is_force flags are true, then the set of + // valid l-value types is slightly modified to accommodate + // the Verilog procedural continuous assignment statements. virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, + bool is_cassign, bool is_force) const; // This attempts to evaluate a constant expression, and return @@ -216,6 +217,7 @@ class PEConcat : public PExpr { unsigned flags) const; virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, + bool is_cassign, bool is_force) const; virtual bool is_collapsible_net(Design*des, NetScope*scope) const; private: @@ -316,6 +318,7 @@ class PEIdent : public PExpr { // Identifiers are also allowed as procedural assignment l-values. virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, + bool is_cassign, bool is_force) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, @@ -376,7 +379,9 @@ class PEIdent : public PExpr { private: NetAssign_*elaborate_lval_method_class_member_(Design*, NetScope*) const; - NetAssign_*elaborate_lval_net_word_(Design*, NetScope*, NetNet*) const; + NetAssign_*elaborate_lval_net_word_(Design*, NetScope*, NetNet*, + bool is_cassign, + bool is_force) const; bool elaborate_lval_net_bit_(Design*, NetScope*, NetAssign_*) const; bool elaborate_lval_net_part_(Design*, NetScope*, NetAssign_*) const; bool elaborate_lval_net_idx_(Design*, NetScope*, NetAssign_*, @@ -563,6 +568,7 @@ class PENumber : public PExpr { unsigned expr_wid, unsigned) const; virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, + bool is_cassign, bool is_force) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; diff --git a/elab_expr.cc b/elab_expr.cc index edb0c163a..d82b82b11 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -3759,26 +3759,38 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, // "unpacked_indices" array. listunpacked_indices; list unpacked_indices_const; - bool flag = indices_to_expressions(des, scope, this, - name_tail.index, net->unpacked_dimensions(), - need_const, - unpacked_indices, - unpacked_indices_const); + indices_flags idx_flags; + indices_to_expressions(des, scope, this, + name_tail.index, net->unpacked_dimensions(), + need_const, + idx_flags, + unpacked_indices, + unpacked_indices_const); NetExpr*canon_index = 0; - if (flag) { + if (idx_flags.invalid) { + // Nothing to do. + + } else if (idx_flags.undefined) { + cerr << get_fileline() << ": warning: " + << "returning 'bx for undefined array access " + << net->name() << as_indices(unpacked_indices) + << "." << endl; + + } else if (idx_flags.variable) { + ivl_assert(*this, unpacked_indices.size() == net->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(net, unpacked_indices); + + } else { ivl_assert(*this, unpacked_indices_const.size() == net->unpacked_dimensions()); canon_index = normalize_variable_unpacked(net, unpacked_indices_const); if (canon_index == 0) { cerr << get_fileline() << ": warning: " << "returning 'bx for out of bounds array access " - << net->name() << as_indices(unpacked_indices_const) << "." << endl; + << net->name() << as_indices(unpacked_indices_const) + << "." << endl; } - - } else { - ivl_assert(*this, unpacked_indices.size() == net->unpacked_dimensions()); - canon_index = normalize_variable_unpacked(net, unpacked_indices); } if (canon_index == 0) { diff --git a/elab_lval.cc b/elab_lval.cc index 41093b14b..084f1fa87 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -71,7 +71,7 @@ * is to try to make a net elaboration, and see if the result is * suitable for assignment. */ -NetAssign_* PExpr::elaborate_lval(Design*, NetScope*, bool) const +NetAssign_* PExpr::elaborate_lval(Design*, NetScope*, bool, bool) const { NetNet*ll = 0; if (ll == 0) { @@ -99,6 +99,7 @@ NetAssign_* PExpr::elaborate_lval(Design*, NetScope*, bool) const */ NetAssign_* PEConcat::elaborate_lval(Design*des, NetScope*scope, + bool is_cassign, bool is_force) const { if (repeat_) { @@ -119,7 +120,8 @@ NetAssign_* PEConcat::elaborate_lval(Design*des, continue; } - NetAssign_*tmp = parms_[idx]->elaborate_lval(des, scope, is_force); + NetAssign_*tmp = parms_[idx]->elaborate_lval(des, scope, + is_cassign, is_force); /* If the l-value doesn't elaborate, the error was already detected and printed. We just skip it and let @@ -152,6 +154,7 @@ NetAssign_* PEConcat::elaborate_lval(Design*des, */ NetAssign_* PEIdent::elaborate_lval(Design*des, NetScope*scope, + bool is_cassign, bool is_force) const { NetNet* reg = 0; @@ -263,7 +266,8 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, ivl_assert(*this, method_name.nil()); if (reg->unpacked_dimensions() > 0) - return elaborate_lval_net_word_(des, scope, reg); + return elaborate_lval_net_word_(des, scope, reg, + is_cassign, is_force); // This must be after the array word elaboration above! if (reg->get_scalar() && @@ -346,7 +350,9 @@ NetAssign_* PEIdent::elaborate_lval_method_class_member_(Design*, NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, NetScope*scope, - NetNet*reg) const + NetNet*reg, + bool is_cassign, + bool is_force) const { const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); @@ -373,30 +379,49 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, // "unpacked_indices" array. listunpacked_indices; list unpacked_indices_const; - bool flag = indices_to_expressions(des, scope, this, - name_tail.index, reg->unpacked_dimensions(), - false, - unpacked_indices, - unpacked_indices_const); + indices_flags flags; + indices_to_expressions(des, scope, this, + name_tail.index, reg->unpacked_dimensions(), + false, + flags, + unpacked_indices, + unpacked_indices_const); NetExpr*canon_index = 0; - if (flag) { - ivl_assert(*this, unpacked_indices_const.size() == reg->unpacked_dimensions()); - canon_index = normalize_variable_unpacked(reg, unpacked_indices_const); - if (canon_index == 0) { - cerr << get_fileline() << ": warning: " - << "ignoring out of bounds l-value array access " << reg->name(); - for (list::const_iterator cur = unpacked_indices_const.begin() - ; cur != unpacked_indices_const.end() ; ++cur) { - cerr << "[" << *cur << "]"; - } - cerr << "." << endl; + if (flags.invalid) { + // Nothing to do. + + } else if (flags.undefined) { + cerr << get_fileline() << ": warning: " + << "ignoring undefined l-value array access " + << reg->name() << as_indices(unpacked_indices) + << "." << endl; + + } else if (flags.variable) { + if (is_cassign || is_force) { + cerr << get_fileline() << ": error: array '" << reg->name() + << "' index must be a constant in this context." << endl; + des->errors += 1; + return 0; } - } else { ivl_assert(*this, unpacked_indices.size() == reg->unpacked_dimensions()); canon_index = normalize_variable_unpacked(reg, unpacked_indices); + + } else { + ivl_assert(*this, unpacked_indices_const.size() == reg->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(reg, unpacked_indices_const); + + if (canon_index == 0) { + cerr << get_fileline() << ": warning: " + << "ignoring out of bounds l-value array access " + << reg->name() << as_indices(unpacked_indices_const) + << "." << endl; + } } + // Ensure invalid array accesses are ignored. + if (canon_index == 0) + canon_index = new NetEConst(verinum(verinum::Vx)); NetAssign_*lv = new NetAssign_(reg); lv->set_word(canon_index); @@ -959,7 +984,7 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope, return false; } -NetAssign_* PENumber::elaborate_lval(Design*des, NetScope*, bool) const +NetAssign_* PENumber::elaborate_lval(Design*des, NetScope*, bool, bool) const { cerr << get_fileline() << ": error: Constant values not allowed " << "in l-value expressions." << endl; diff --git a/elab_net.cc b/elab_net.cc index 6d39f8f4c..db4adc788 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -581,35 +581,49 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, // Evaluate all the index expressions into an // "unpacked_indices" array. listunpacked_indices; - bool flag = indices_to_expressions(des, scope, this, - path_tail.index, sig->unpacked_dimensions(), - true, - unpacked_indices, - unpacked_indices_const); - // Note that !flag includes that there were any other - // elaboration errors generating the unpacked_indices list. - if (!flag) { - cerr << get_fileline() << ": error: array " << sig->name() - << " index must be a constant in this context." << endl; + indices_flags flags; + indices_to_expressions(des, scope, this, + path_tail.index, sig->unpacked_dimensions(), + true, + flags, + unpacked_indices, + unpacked_indices_const); + + if (flags.invalid) { + return 0; + + } else if (flags.variable) { + cerr << get_fileline() << ": error: array '" << sig->name() + << "' index must be a constant in this context." << endl; des->errors += 1; return 0; - } - NetExpr*canon_index = 0; - ivl_assert(*this, unpacked_indices_const.size() == sig->unpacked_dimensions()); - canon_index = normalize_variable_unpacked(sig, unpacked_indices_const); - if (canon_index == 0) { - // Normalize detected an out-of-bounds - // index. Indicate that by setting the generated - // widx to -1. + } else if (flags.undefined) { + cerr << get_fileline() << ": warning: " + << "ignoring undefined l-value array access " + << sig->name() << as_indices(unpacked_indices) + << "." << endl; widx = -1; } else { - NetEConst*canon_const = dynamic_cast(canon_index); - ivl_assert(*this, canon_const); + NetExpr*canon_index = 0; + ivl_assert(*this, unpacked_indices_const.size() == sig->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(sig, unpacked_indices_const); - widx = canon_const->value().as_long(); - delete canon_index; + if (canon_index == 0) { + cerr << get_fileline() << ": warning: " + << "ignoring out of bounds l-value array access " + << sig->name() << as_indices(unpacked_indices_const) + << "." << endl; + widx = -1; + + } else { + NetEConst*canon_const = dynamic_cast(canon_index); + ivl_assert(*this, canon_const); + + widx = canon_const->value().as_long(); + delete canon_index; + } } if (debug_elaborate) @@ -681,12 +695,8 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, } if (sig->pin_count() > 1) { - if (widx < 0 || widx >= (long) sig->pin_count()) { - cerr << get_fileline() << ": warning: ignoring out of " - "bounds l-value array access " - << sig->name() << as_indices(unpacked_indices_const) << "." << endl; + if (widx < 0 || widx >= (long) sig->pin_count()) return 0; - } netvector_t*tmp2_vec = new netvector_t(sig->data_type(), sig->vector_width()-1,0); diff --git a/elaborate.cc b/elaborate.cc index b0cb3a8fc..29f1cca24 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -2213,7 +2213,7 @@ NetProc* Statement::elaborate(Design*des, NetScope*) const NetAssign_* PAssign_::elaborate_lval(Design*des, NetScope*scope) const { assert(lval_); - return lval_->elaborate_lval(des, scope, false); + return lval_->elaborate_lval(des, scope, false, false); } NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, @@ -3483,7 +3483,7 @@ NetProc* PCallTask::elaborate_build_call_(Design*des, NetScope*scope, detailed message. */ NetAssign_*lv; if (parms_[idx]) { - lv = parms_[idx]->elaborate_lval(des, scope, false); + lv = parms_[idx]->elaborate_lval(des, scope, false, false); if (lv == 0) { cerr << parms_[idx]->get_fileline() << ": error: " << "I give up on task port " << (idx+1) @@ -3564,7 +3564,7 @@ NetCAssign* PCAssign::elaborate(Design*des, NetScope*scope) const return 0; } - NetAssign_*lval = lval_->elaborate_lval(des, scope, false); + NetAssign_*lval = lval_->elaborate_lval(des, scope, true, false); if (lval == 0) return 0; @@ -3601,7 +3601,7 @@ NetDeassign* PDeassign::elaborate(Design*des, NetScope*scope) const return 0; } - NetAssign_*lval = lval_->elaborate_lval(des, scope, false); + NetAssign_*lval = lval_->elaborate_lval(des, scope, true, false); if (lval == 0) return 0; @@ -4212,7 +4212,7 @@ NetForce* PForce::elaborate(Design*des, NetScope*scope) const return 0; } - NetAssign_*lval = lval_->elaborate_lval(des, scope, true); + NetAssign_*lval = lval_->elaborate_lval(des, scope, false, true); if (lval == 0) return 0; @@ -4400,7 +4400,7 @@ NetProc* PRelease::elaborate(Design*des, NetScope*scope) const return 0; } - NetAssign_*lval = lval_->elaborate_lval(des, scope, true); + NetAssign_*lval = lval_->elaborate_lval(des, scope, false, true); if (lval == 0) return 0; diff --git a/netmisc.cc b/netmisc.cc index 711abd486..5a09029a6 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -458,11 +458,9 @@ ostream& operator << (ostream&o, __IndicesManip val) * The src is the input index expression list from the expression, and * the count is the number that are to be elaborated into the indices * list. At the same time, create a indices_const list that contains - * the evaluated values for the expression, if they can be - * evaluated. This function will return "true" if all the constants - * can be evaluated. + * the evaluated values for the expression, if they can be evaluated. */ -bool indices_to_expressions(Design*des, NetScope*scope, +void indices_to_expressions(Design*des, NetScope*scope, // loc is for error messages. const LineInfo*loc, // src is the index list, and count is @@ -471,11 +469,14 @@ bool indices_to_expressions(Design*des, NetScope*scope, // True if the expression MUST be constant. bool need_const, // These are the outputs. + indices_flags&flags, list&indices, list&indices_const) { ivl_assert(*loc, count <= src.size()); - bool flag = true; + flags.invalid = false; + flags.variable = false; + flags.undefined = false; for (list::const_iterator cur = src.begin() ; count > 0 ; ++cur, --count) { ivl_assert(*loc, cur->sel != index_component_t::SEL_NONE); @@ -489,25 +490,21 @@ bool indices_to_expressions(Design*des, NetScope*scope, NetExpr*word_index = elab_and_eval(des, scope, cur->msb, -1, need_const); - // If the elaboration failed, then it is most certainly - // not constant, either. if (word_index == 0) - flag = false; + flags.invalid = true; // Track if we detect any non-constant expressions // here. This may allow for a special case. - if (flag) { - NetEConst*word_const = dynamic_cast (word_index); - if (word_const) - indices_const.push_back(word_const->value().as_long()); - else - flag = false; - } + NetEConst*word_const = dynamic_cast (word_index); + if (word_const == 0) + flags.variable = true; + else if (!word_const->value().is_defined()) + flags.undefined = true; + else if (!flags.variable && !flags.undefined) + indices_const.push_back(word_const->value().as_long()); indices.push_back(word_index); } - - return flag; } static void make_strides(const vector&dims, @@ -1237,9 +1234,10 @@ NetExpr*collapse_array_exprs(Design*des, NetScope*scope, // First elaborate all the expressions as far as possible. list exprs; list exprs_const; + indices_flags flags; indices_to_expressions(des, scope, loc, indices, net->packed_dimensions(), - false, exprs, exprs_const); + false, flags, exprs, exprs_const); ivl_assert(*loc, exprs.size() == net->packed_dimensions()); // Special Case: there is only 1 packed dimension, so the diff --git a/netmisc.h b/netmisc.h index f413a38fe..384bd4137 100644 --- a/netmisc.h +++ b/netmisc.h @@ -166,7 +166,12 @@ extern ostream& operator << (ostream&o, __IndicesManip); * Given a list of index expressions, generate elaborated expressions * and constant values, if possible. */ -extern bool indices_to_expressions(Design*des, NetScope*scope, +struct indices_flags { + bool invalid; // at least one index failed elaboration + bool variable; // at least one index is a dynamic value + bool undefined; // at least one index is an undefined value +}; +extern void indices_to_expressions(Design*des, NetScope*scope, // loc is for error messages. const LineInfo*loc, // src is the index list, and count is @@ -175,7 +180,8 @@ extern bool indices_to_expressions(Design*des, NetScope*scope, // True if the expression MUST be constant. bool need_const, // These are the outputs. - list&indices, list&indices_const); + indices_flags&flags, + list&indices,list&indices_const); extern NetExpr*normalize_variable_unpacked(const NetNet*net, list&indices); extern NetExpr*normalize_variable_unpacked(const NetNet*net, list&indices); diff --git a/tgt-vvp/stmt_assign.c b/tgt-vvp/stmt_assign.c index 2ef99a246..e98ec1d1f 100644 --- a/tgt-vvp/stmt_assign.c +++ b/tgt-vvp/stmt_assign.c @@ -343,10 +343,13 @@ static void set_vec_to_lval_slice(ivl_lval_t lval, unsigned bit, unsigned wid) /* If the word index is a constant expression, then evaluate it to select the word, and pay no further heed to the - expression itself. */ - if (word_ix && number_is_immediate(word_ix, IMM_WID, 0)) { - assert(! number_is_unknown(word_ix)); + expression itself. Out-of-bounds and undefined indices are + converted to a canonical index of 'bx during elaboration, + and we don't try to optimise that case. */ + if (word_ix && number_is_immediate(word_ix, IMM_WID, 0) && + !number_is_unknown(word_ix)) { use_word = get_number_immediate(word_ix); + assert(use_word < ivl_signal_array_count(sig)); word_ix = 0; } @@ -437,16 +440,10 @@ static void set_vec_to_lval_slice(ivl_lval_t lval, unsigned bit, unsigned wid) /* If the word index is a constant, then we can write directly to the word and save the index calculation. */ if (word_ix == 0) { - if (use_word < ivl_signal_array_count(sig)) { - fprintf(vvp_out, " %%ix/load 1, 0, 0;\n"); - fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", - use_word); - fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", - sig, bit, wid); - } else { - fprintf(vvp_out, " ; %%set/v v%p_%lu, %u, %u " - "OUT OF BOUNDS\n", sig, use_word, bit, wid); - } + fprintf(vvp_out, " %%ix/load 1, 0, 0;\n"); + fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); + fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", + sig, bit, wid); } else { unsigned skip_set = transient_id++; @@ -711,12 +708,34 @@ static int show_stmt_assign_sig_real(ivl_statement_t net) // For now, only support 1-dimensional arrays. assert(ivl_signal_dimensions(var) == 1); - // Calculate the word index into an index register ivl_expr_t word_ex = ivl_lval_idx(lval); int word_ix = allocate_word(); - draw_eval_expr_into_integer(word_ex, word_ix); - // Generate an assignment to write to the array. - fprintf(vvp_out, " %%store/reala v%p, %d;\n", var, word_ix); + + /* If the word index is a constant, then we can write + directly to the word and save the index calculation. + Out-of-bounds and undefined indices are converted to + a canonical index of 'bx during elaboration, and we + don't try to optimise that case. */ + if (word_ex && number_is_immediate(word_ex, IMM_WID, 0) && + !number_is_unknown(word_ex)) { + unsigned long use_word = get_number_immediate(word_ex); + assert(use_word < ivl_signal_array_count(var)); + fprintf(vvp_out, " %%ix/load %u, %lu, 0;\n", + word_ix, use_word); + fprintf(vvp_out, " %%store/reala v%p, %d;\n", + var, word_ix); + + } else { + unsigned do_store = transient_id++; + unsigned end_store = transient_id++; + draw_eval_expr_into_integer(word_ex, word_ix); + fprintf(vvp_out, " %%jmp/0 t_%u, 4;\n", do_store); + fprintf(vvp_out, " %%pop/real 1;\n"); + fprintf(vvp_out, " %%jmp t_%u;\n", end_store); + fprintf(vvp_out, "t_%u ;\n", do_store); + fprintf(vvp_out, " %%store/reala v%p, %d;\n", var, word_ix); + fprintf(vvp_out, "t_%u ;\n", end_store); + } clr_word(word_ix); diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index 784c883ad..b4d4aa799 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -45,18 +45,22 @@ 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 skip_assign = transient_id++; + unsigned end_assign = transient_id++; /* This code is common to all the different types of array delays. */ - if (number_is_immediate(word_ix, IMM_WID, 0)) { - assert(! number_is_unknown(word_ix)); + if (number_is_immediate(word_ix, IMM_WID, 0) && + !number_is_unknown(word_ix)) { fprintf(vvp_out, " %%ix/load 3, %lu, 0; address\n", get_number_immediate(word_ix)); } else { /* Calculate array word index into index register 3 */ draw_eval_expr_into_integer(word_ix, 3); /* Skip assignment if word expression is not defined. */ - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign); + 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) { @@ -91,7 +95,7 @@ static void assign_to_array_r_word(ivl_signal_t lsig, ivl_expr_t word_ix, } } - fprintf(vvp_out, "t_%u ;\n", skip_assign); + fprintf(vvp_out, "t_%u ;\n", end_assign); if (nevents != 0) fprintf(vvp_out, " %%evctl/c;\n"); clear_expression_lookaside(); @@ -107,15 +111,15 @@ static void assign_to_array_word(ivl_signal_t lsig, ivl_expr_t word_ix, unsigned long part_off = 0; if (part_off_ex == 0) { part_off = 0; - } else if (number_is_immediate(part_off_ex, IMM_WID, 0)) { - assert(! number_is_unknown(part_off_ex)); + } else if (number_is_immediate(part_off_ex, IMM_WID, 0) && + !number_is_unknown(part_off_ex)) { part_off = get_number_immediate(part_off_ex); part_off_ex = 0; } /* This code is common to all the different types of array delays. */ - if (number_is_immediate(word_ix, IMM_WID, 0)) { - assert(! number_is_unknown(word_ix)); + if (number_is_immediate(word_ix, IMM_WID, 0) && + !number_is_unknown(word_ix)) { fprintf(vvp_out, " %%ix/load 3, %lu, 0; address\n", get_number_immediate(word_ix)); } else { @@ -840,12 +844,36 @@ static void force_real_to_lval(ivl_statement_t net) assert(ivl_lval_width(lval) == 1); assert(ivl_lval_part_off(lval) == 0); - assert(ivl_lval_idx(lval) == 0); + + ivl_expr_t word_idx = ivl_lval_idx(lval); + unsigned long use_word = 0; + if (word_idx != 0) { + assert(number_is_immediate(word_idx, IMM_WID, 0)); + /* An out-of-range or undefined index will have been + converted to a canonical offset of 1'bx. Skip the + assignment in this case. */ + if (number_is_unknown(word_idx)) { + fprintf(vvp_out, " %%pop/real 1;\n"); + return; + } + use_word = get_number_immediate(word_idx); + + /* We do not currently support using a word from a variable + array as the L-value (SystemVerilog / Icarus extension). */ + if (ivl_signal_type(lsig) == IVL_SIT_REG) { + fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s to the " + "word of a variable array (%s[%ld]).\n", + ivl_stmt_file(net), ivl_stmt_lineno(net), + command_name, ivl_signal_basename(lsig), + ivl_signal_array_base(lsig) + (long)use_word); + vvp_errors += 1; + } + } + /* L-Value must be a signal: reg or wire */ assert(lsig != 0); - fprintf(vvp_out, " %s v%p_0;\n", command_name, lsig); - + fprintf(vvp_out, " %s v%p_%lu;\n", command_name, lsig, use_word); } static void force_vector_to_lval(ivl_statement_t net, struct vector_info rvec) @@ -886,14 +914,33 @@ static void force_vector_to_lval(ivl_statement_t net, struct vector_info rvec) part_off = 0; } else { assert(number_is_immediate(part_off_ex, IMM_WID, 0)); - assert(! number_is_unknown(part_off_ex)); + /* An out-of-range or undefined offset will have been + converted to a canonical offset of 1'bx. Skip the + assignment in this case. */ + if (number_is_unknown(part_off_ex)) + return; part_off = get_number_immediate(part_off_ex); } if (word_idx != 0) { assert(number_is_immediate(word_idx, IMM_WID, 0)); - assert(! number_is_unknown(word_idx)); + /* An out-of-range or undefined index will have been + converted to a canonical offset of 1'bx. Skip the + assignment in this case. */ + if (number_is_unknown(word_idx)) + return; use_word = get_number_immediate(word_idx); + + /* We do not currently support using a word from a variable + array as the L-value (SystemVerilog / Icarus extension). */ + if (ivl_signal_type(lsig) == IVL_SIT_REG) { + fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s to the " + "word of a variable array (%s[%ld]).\n", + ivl_stmt_file(net), ivl_stmt_lineno(net), + command_name, ivl_signal_basename(lsig), + ivl_signal_array_base(lsig) + (long)use_word); + vvp_errors += 1; + } } /* L-Value must be a signal: reg or wire */ @@ -1000,10 +1047,14 @@ static void force_link_rval(ivl_statement_t net, ivl_expr_t rval) /* At least for now, only handle force to fixed words of an array. */ if ((lword_idx = ivl_lval_idx(lval)) != 0) { assert(number_is_immediate(lword_idx, IMM_WID, 0)); - assert(! number_is_unknown(lword_idx)); + /* An out-of-range or undefined index will have been + converted to a canonical offset of 1'bx. Skip the + assignment in this case. */ + if (number_is_unknown(lword_idx)) + return; use_lword = get_number_immediate(lword_idx); /* We do not currently support using a word from a variable - * array as the L-value (Icarus extension). */ + * array as the L-value (SystemVerilog / Icarus extension). */ if (ivl_signal_type(lsig) == IVL_SIT_REG) { /* Normalize the array access. */ long real_word = use_lword; @@ -1019,7 +1070,11 @@ static void force_link_rval(ivl_statement_t net, ivl_expr_t rval) if ((rword_idx = ivl_expr_oper1(rval)) != 0) { assert(ivl_signal_dimensions(rsig) != 0); assert(number_is_immediate(rword_idx, IMM_WID, 0)); - assert(! number_is_unknown(rword_idx)); + /* An out-of-range or undefined index will have been + converted to a canonical offset of 1'bx. Skip the + assignment in this case. */ + if (number_is_unknown(rword_idx)) + return; use_rword = get_number_immediate(rword_idx); /* We do not currently support using a word from a variable * array as the R-value. */ @@ -1093,9 +1148,20 @@ static int show_stmt_deassign(ivl_statement_t net) lval = ivl_stmt_lval(net, 0); assert(ivl_lval_width(lval) == 1); assert(ivl_lval_part_off(lval) == 0); - assert(ivl_lval_idx(lval) == 0); - fprintf(vvp_out, " %%deassign/wr v%p_0;\n", sig); + ivl_expr_t word_idx = ivl_lval_idx(lval); + unsigned long use_word = 0; + if (word_idx != 0) { + assert(number_is_immediate(word_idx, IMM_WID, 0)); + /* An out-of-range or undefined index will have been + converted to a canonical offset of 1'bx. Skip the + deassignment in this case. */ + if (number_is_unknown(word_idx)) + return 0; + use_word = get_number_immediate(word_idx); + } + + fprintf(vvp_out, " %%deassign/wr v%p_%lu;\n", sig, use_word); return 0; } @@ -1123,7 +1189,11 @@ static int show_stmt_deassign(ivl_statement_t net) if (word_idx != 0) { assert(number_is_immediate(word_idx, IMM_WID, 0)); - assert(! number_is_unknown(word_idx)); + /* An out-of-range or undefined index will have been + converted to a canonical offset of 1'bx. Skip the + deassignment in this case. */ + if (number_is_unknown(word_idx)) + return 0; use_word = get_number_immediate(word_idx); } @@ -1432,11 +1502,23 @@ static int show_stmt_release(ivl_statement_t net) lval = ivl_stmt_lval(net, 0); assert(ivl_lval_width(lval) == 1); assert(ivl_lval_part_off(lval) == 0); - assert(ivl_lval_idx(lval) == 0); + + ivl_expr_t word_idx = ivl_lval_idx(lval); + unsigned long use_word = 0; + if (word_idx != 0) { + assert(number_is_immediate(word_idx, IMM_WID, 0)); + /* An out-of-range or undefined index will have been + converted to a canonical offset of 1'bx. Skip the + deassignment in this case. */ + if (number_is_unknown(word_idx)) + return 0; + use_word = get_number_immediate(word_idx); + } if (ivl_signal_type(sig) == IVL_SIT_REG) type = 1; - fprintf(vvp_out, " %%release/wr v%p_0, %u;\n", sig, type); + fprintf(vvp_out, " %%release/wr v%p_%lu, %u;\n", + sig, use_word, type); return 0; } @@ -1459,7 +1541,11 @@ static int show_stmt_release(ivl_statement_t net) part_off = 0; if (part_off_ex != 0) { assert(number_is_immediate(part_off_ex, 64, 0)); - assert(! number_is_unknown(part_off_ex)); + /* An out-of-range or undefined offset will have been + converted to a canonical offset of 1'bx. Skip the + assignment in this case. */ + if (number_is_unknown(part_off_ex)) + return 0; part_off = get_number_immediate(part_off_ex); } @@ -1474,7 +1560,11 @@ static int show_stmt_release(ivl_statement_t net) if (word_idx != 0) { assert(number_is_immediate(word_idx, IMM_WID, 0)); - assert(! number_is_unknown(word_idx)); + /* An out-of-range or undefined index will have been + converted to a canonical offset of 1'bx. Skip the + assignment in this case. */ + if (number_is_unknown(word_idx)) + return 0; use_word = get_number_immediate(word_idx); }