From 2b17366ad5d59b458042c15311c3cb5bcd467553 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 28 Aug 2009 16:50:59 -0700 Subject: [PATCH] Major rewrite of indexed part selects. This patch is a major rewrite of the indexed part selects (+: and -:). It made the following enhancements: 1. Make indexed part selects work correctly with both big and little endian vectors. 2. Add a warning flag that warns about constant out of bounds/or 'bx indexed selects. 3. Moved the -: parameter code to its own routine. 4. Added support for straddling before part selects in a CA. 5. Added more assert(! number_is_unknown) statements. 6. Add warning for &PV<> select with a signed index signal that is less than the width of an int. This will be fixed later. 7. Add support for loading a 'bx/'bz constant into a numeric register. 8. Add a number of signed value fixes to the compiler/code generator. 9. Major fix of draw_select_expr() in the code generator. --- PExpr.h | 6 + elab_expr.cc | 311 ++++++++++++++++++++++++++++++++++---------- elab_lval.cc | 80 ++++++++++-- elab_net.cc | 46 +++++-- netlist.cc | 9 +- netlist.h | 4 +- tgt-vvp/draw_vpi.c | 15 ++- tgt-vvp/eval_expr.c | 109 +++++++++++++--- tgt-vvp/vvp_scope.c | 9 ++ 9 files changed, 482 insertions(+), 107 deletions(-) diff --git a/PExpr.h b/PExpr.h index 3cef1cee8..00c3c2fee 100644 --- a/PExpr.h +++ b/PExpr.h @@ -339,6 +339,12 @@ class PEIdent : public PExpr { NetScope*found, const NetExpr*par_msb, const NetExpr*par_lsb) const; + NetExpr*elaborate_expr_param_idx_do_(Design*des, + NetScope*scope, + const NetExpr*par, + NetScope*found, + const NetExpr*par_msb, + const NetExpr*par_lsb) const; NetExpr*elaborate_expr_net(Design*des, NetScope*scope, NetNet*net, diff --git a/elab_expr.cc b/elab_expr.cc index b40044af5..261539812 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -2210,12 +2210,12 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, } static verinum param_part_select_bits(const verinum&par_val, long wid, - long lsv, long par_lsv) + long lsv) { verinum result (verinum::Vx, wid, true); for (long idx = 0 ; idx < wid ; idx += 1) { - long off = idx + lsv - par_lsv; + long off = idx + lsv; if (off < 0) result.set(idx, verinum::Vx); else if (off < (long)par_val.len()) @@ -2230,7 +2230,7 @@ static verinum param_part_select_bits(const verinum&par_val, long wid, // If the input is a string, and the part select is working on // byte boundaries, then make the result into a string. - if (par_val.is_string() && (labs(lsv-par_lsv)%8 == 0) && (wid%8 == 0)) + if (par_val.is_string() && (labs(lsv)%8 == 0) && (wid%8 == 0)) return result.as_string(); return result; @@ -2286,28 +2286,56 @@ NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope, const NetEConst*par_ex = dynamic_cast (par); ivl_assert(*this, par_ex); - verinum result = param_part_select_bits(par_ex->value(), wid, lsv, par_lsv); + verinum result = param_part_select_bits(par_ex->value(), wid, lsv-par_lsv); NetEConst*result_ex = new NetEConst(result); result_ex->set_line(*this); return result_ex; } +static void warn_param_ob(long par_msv, long par_lsv, bool defined, + long par_base, unsigned long wid, long pwid, + const LineInfo *info, perm_string name, bool up) +{ + long par_max; + + if (defined) { + if (par_msv < par_lsv) par_max = par_lsv-par_msv; + else par_max = par_msv-par_lsv; + } else { + if (pwid < 0) par_max = integer_width; + else par_max = pwid; + } + + /* Is this a select before the start of the parameter? */ + if (par_base < 0) { + cerr << info->get_fileline() << ": warning: " << name << "[" + << par_base; + if (up) cerr << "+:"; + else cerr << "-:"; + cerr << wid << "] is selecting before vector." << endl; + } + + /* Is this a select after the end of the parameter? */ + if (par_base + (long)wid - 1 > par_max) { + cerr << info->get_fileline() << ": warning: " << name << "[" + << par_base << "+:" << wid << "] is selecting after vector." + << endl; + } +} + NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb) const { - long par_msv, par_lsv; - bool flag = calculate_param_range_(des, scope, par_msb, par_msv, par_lsb, par_lsv); - if (!flag) - return 0; + if(! calculate_param_range_(des, scope, par_msb, par_msv, + par_lsb, par_lsv)) return 0; NetExpr*base = calculate_up_do_base_(des, scope); - if (base == 0) - return 0; + if (base == 0) return 0; unsigned long wid = 0; calculate_up_do_width_(des, scope, wid); @@ -2323,22 +2351,139 @@ NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope, // Handle the special case that the base is constant. In this // case, just precalculate the entire constant result. if (NetEConst*base_c = dynamic_cast (base)) { + if (! base_c->value().is_defined()) { + NetEConst *ex; + ex = new NetEConst(verinum(verinum::Vx, wid, true)); + ex->set_line(*this); + if (warn_ob_select) { + perm_string name = peek_tail_name(path_); + cerr << get_fileline() << ": warning: " << name + << "['bx+:" << wid + << "] is always outside vector." << endl; + } + return ex; + } long lsv = base_c->value().as_long(); + long par_base = par_lsv; // Watch out for reversed bit numbering. We're making // the part select from LSB to MSB. - if (par_msv < par_lsv) - lsv = lsv - wid + 1; + if (par_msv < par_lsv) { + par_base = lsv; + lsv = par_lsv - wid + 1; + } + if (warn_ob_select) { + bool defined = true; + // Check to see if the parameter has a defined range. + if (par_msb == 0) { + assert(par_lsb == 0); + defined = false; + } + // Get the parameter values width. + long pwid = -1; + if (par_ex->has_width()) pwid = par_ex->expr_width()-1; + perm_string name = peek_tail_name(path_); + warn_param_ob(par_msv, par_lsv, defined, lsv-par_base, wid, + pwid, this, name, true); + } verinum result = param_part_select_bits(par_ex->value(), wid, - lsv, par_lsv); + lsv-par_base); NetEConst*result_ex = new NetEConst(result); result_ex->set_line(*this); return result_ex; } - if ((par_msv < par_lsv) && (wid>1)) - base = make_add_expr(base, 1-(long)wid); + if (par_msv >= par_lsv) { + if (par_lsv != 0) base = make_add_expr(base, -par_lsv); + } else { + base = make_sub_expr(par_lsv-wid+1, base); + } + + NetExpr*tmp = par->dup_expr(); + tmp = new NetESelect(tmp, base, wid); + tmp->set_line(*this); + return tmp; +} + +NetExpr* PEIdent::elaborate_expr_param_idx_do_(Design*des, NetScope*scope, + const NetExpr*par, + NetScope*found_in, + const NetExpr*par_msb, + const NetExpr*par_lsb) const +{ + long par_msv, par_lsv; + if(! calculate_param_range_(des, scope, par_msb, par_msv, + par_lsb, par_lsv)) return 0; + + NetExpr*base = calculate_up_do_base_(des, scope); + if (base == 0) return 0; + + unsigned long wid = 0; + calculate_up_do_width_(des, scope, wid); + + const NetEConst*par_ex = dynamic_cast (par); + ivl_assert(*this, par_ex); + + if (debug_elaborate) + cerr << get_fileline() << ": debug: Calculate part select " + << "[" << *base << "-:" << wid << "] from range " + << "[" << par_msv << ":" << par_lsv << "]." << endl; + + // Handle the special case that the base is constant. In this + // case, just precalculate the entire constant result. + if (NetEConst*base_c = dynamic_cast (base)) { + if (! base_c->value().is_defined()) { + NetEConst *ex; + ex = new NetEConst(verinum(verinum::Vx, wid, true)); + ex->set_line(*this); + if (warn_ob_select) { + perm_string name = peek_tail_name(path_); + cerr << get_fileline() << ": warning: " << name + << "['bx-:" << wid + << "] is always outside vector." << endl; + } + return ex; + } + long lsv = base_c->value().as_long(); + long par_base = par_lsv + wid - 1; + + // Watch out for reversed bit numbering. We're making + // the part select from LSB to MSB. + if (par_msv < par_lsv) { + par_base = lsv; + lsv = par_lsv; + } + + if (warn_ob_select) { + bool defined = true; + // Check to see if the parameter has a defined range. + if (par_msb == 0) { + assert(par_lsb == 0); + defined = false; + } + // Get the parameter values width. + long pwid = -1; + if (par_ex->has_width()) pwid = par_ex->expr_width()-1; + perm_string name = peek_tail_name(path_); + warn_param_ob(par_msv, par_lsv, defined, lsv-par_base, wid, + pwid, this, name, false); + } + + verinum result = param_part_select_bits(par_ex->value(), wid, + lsv-par_base); + NetEConst*result_ex = new NetEConst(result); + result_ex->set_line(*this); + return result_ex; + } + + if (par_msv >= par_lsv) { + if (long offset = par_lsv+wid-1) { + base = make_add_expr(base, -offset); + } + } else { + base = make_sub_expr(par_lsv, base); + } NetExpr*tmp = par->dup_expr(); tmp = new NetESelect(tmp, base, wid); @@ -2385,49 +2530,16 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, return elaborate_expr_param_idx_up_(des, scope, par, found_in, par_msb, par_lsb); + if (use_sel == index_component_t::SEL_IDX_DO) + return elaborate_expr_param_idx_do_(des, scope, par, found_in, + par_msb, par_lsb); + // NOTE TO SELF (continued): The code below should be // rewritten in the above format, as I get to it. NetExpr*tmp = par->dup_expr(); - if (use_sel == index_component_t::SEL_IDX_DO) { - - ivl_assert(*this, !name_tail.index.empty()); - const index_component_t&index_tail = name_tail.index.back(); - ivl_assert(*this, index_tail.msb); - ivl_assert(*this, index_tail.lsb); - - /* Get and evaluate the width of the index - select. This must be constant. */ - need_constant_expr = true; - NetExpr*wid_ex = elab_and_eval(des, scope, index_tail.lsb, -1); - need_constant_expr = false; - NetEConst*wid_ec = dynamic_cast (wid_ex); - if (wid_ec == 0) { - cerr << index_tail.lsb->get_fileline() << ": error: " - << "Second expression of indexed part select " - << "most be constant." << endl; - des->errors += 1; - return 0; - } - - unsigned wid = wid_ec->value().as_ulong(); - - NetExpr*idx_ex = elab_and_eval(des, scope, index_tail.msb, -1); - if (idx_ex == 0) { - return 0; - } - - if (use_sel == index_component_t::SEL_IDX_DO && wid > 1) { - idx_ex = make_add_expr(idx_ex, 1-(long)wid); - } - - - /* Wrap the param expression with a part select. */ - tmp = new NetESelect(tmp, idx_ex, wid); - - - } else if (use_sel == index_component_t::SEL_BIT) { + if (use_sel == index_component_t::SEL_BIT) { ivl_assert(*this, !name_tail.index.empty()); const index_component_t&index_tail = name_tail.index.back(); ivl_assert(*this, index_tail.msb); @@ -2768,15 +2880,47 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, if (net->sig()->sb_to_idx(lsv) == 0 && wid == net->vector_width()) { delete base; + net->cast_signed(false); return net; } + long offset = 0; + if (net->msi() < net->lsi()) { + offset = -wid + 1; + } // Otherwise, make a part select that covers the right // range. - ex = new NetEConst(verinum(net->sig()->sb_to_idx(lsv))); + ex = new NetEConst(verinum(net->sig()->sb_to_idx(lsv) + + offset)); + if (warn_ob_select) { + long rel_base = net->sig()->sb_to_idx(lsv) + offset; + if (rel_base < 0) { + cerr << get_fileline() << ": warning: " + << net->name(); + if (net->word_index()) cerr << "[]"; + cerr << "[" << lsv << "+:" << wid + << "] is selecting before vector." << endl; + } + if (rel_base + wid > net->vector_width()) { + cerr << get_fileline() << ": warning: " + << net->name(); + if (net->word_index()) cerr << "[]"; + cerr << "[" << lsv << "+:" << wid + << "] is selecting after vector." << endl; + } + } } else { // Return 'bx for an undefined base. - ex = new NetEConst(verinum(verinum::Vx, 1, false)); + ex = new NetEConst(verinum(verinum::Vx, wid, true)); + ex->set_line(*this); + delete base; + if (warn_ob_select) { + cerr << get_fileline() << ": warning: " << net->name(); + if (net->word_index()) cerr << "[]"; + cerr << "['bx+:" << wid + << "] is always outside vector." << endl; + } + return ex; } NetESelect*ss = new NetESelect(net, ex, wid); ss->set_line(*this); @@ -2787,11 +2931,9 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, if (net->msi() > net->lsi()) { if (long offset = net->lsi()) - base = make_add_expr(base, 0-offset); + base = make_add_expr(base, -offset); } else { - long vwid = net->lsi() - net->msi() + 1; - long offset = net->msi(); - base = make_sub_expr(vwid-offset-wid, base); + base = make_sub_expr(net->lsi()-wid+1, base); } NetESelect*ss = new NetESelect(net, base, wid); @@ -2830,15 +2972,47 @@ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, if (net->sig()->sb_to_idx(lsv) == (signed) (wid-1) && wid == net->vector_width()) { delete base; + net->cast_signed(false); return net; } + long offset = 0; + if (net->msi() > net->lsi()) { + offset = -wid + 1; + } // Otherwise, make a part select that covers the right // range. - ex = new NetEConst(verinum(net->sig()->sb_to_idx(lsv)-wid+1)); + ex = new NetEConst(verinum(net->sig()->sb_to_idx(lsv) + + offset)); + if (warn_ob_select) { + long rel_base = net->sig()->sb_to_idx(lsv) + offset; + if (rel_base < 0) { + cerr << get_fileline() << ": warning: " + << net->name(); + if (net->word_index()) cerr << "[]"; + cerr << "[" << lsv << "+:" << wid + << "] is selecting before vector." << endl; + } + if (rel_base + wid > net->vector_width()) { + cerr << get_fileline() << ": warning: " + << net->name(); + if (net->word_index()) cerr << "[]"; + cerr << "[" << lsv << "-:" << wid + << "] is selecting after vector." << endl; + } + } } else { // Return 'bx for an undefined base. - ex = new NetEConst(verinum(verinum::Vx, 1, false)); + ex = new NetEConst(verinum(verinum::Vx, wid, true)); + ex->set_line(*this); + delete base; + if (warn_ob_select) { + cerr << get_fileline() << ": warning: " << net->name(); + if (net->word_index()) cerr << "[]"; + cerr << "['bx-:" << wid + << "] is always outside vector." << endl; + } + return ex; } NetESelect*ss = new NetESelect(net, ex, wid); ss->set_line(*this); @@ -2847,16 +3021,19 @@ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, return ss; } - long offset = net->lsi(); - NetExpr*base_adjusted = wid > 1 - ? make_add_expr(base,1-(long)wid-offset) - : (offset == 0? base : make_add_expr(base, 0-offset)); - NetESelect*ss = new NetESelect(net, base_adjusted, wid); + if (net->msi() > net->lsi()) { + if (long offset = net->lsi()+wid-1) + base = make_add_expr(base, -offset); + } else { + base = make_sub_expr(net->lsi(), base); + } + + NetESelect*ss = new NetESelect(net, base, wid); ss->set_line(*this); if (debug_elaborate) { cerr << get_fileline() << ": debug: Elaborate part " - << "select base="<< *base_adjusted << ", wid="<< wid << endl; + << "select base="<< *base << ", wid="<< wid << endl; } return ss; diff --git a/elab_lval.cc b/elab_lval.cc index c421e1243..95808a208 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -476,14 +476,78 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, NetExpr*base = elab_and_eval(des, scope, index_tail.msb, -1); - /* Correct the mux for the range of the vector. */ - if (reg->msb() < reg->lsb()) - base = make_sub_expr(reg->lsb(), base); - else if (reg->lsb() != 0) - base = make_add_expr(base, - reg->lsb()); - - if (use_sel == index_component_t::SEL_IDX_DO && wid > 1 ) { - base = make_add_expr(base, 1-(long)wid); + // Handle the special case that the base is constant. For this + // case we can reduce the expression. + if (NetEConst*base_c = dynamic_cast (base)) { + // For the undefined case just let the constant pass and + // we will handle it in the code generator. + if (base_c->value().is_defined()) { + long lsv = base_c->value().as_long(); + long offset = 0; + if (((reg->msb() < reg->lsb()) && + use_sel == index_component_t::SEL_IDX_UP) || + ((reg->msb() > reg->lsb()) && + use_sel == index_component_t::SEL_IDX_DO)) { + offset = -wid + 1; + } + delete base; + base = new NetEConst(verinum(reg->sb_to_idx(lsv) + offset)); + if (warn_ob_select) { + long rel_base = reg->sb_to_idx(lsv) + offset; + if (rel_base < 0) { + cerr << get_fileline() << ": warning: " << reg->name(); + if (reg->array_dimensions() > 0) cerr << "[]"; + cerr << "[" << lsv; + if (use_sel == index_component_t::SEL_IDX_UP) { + cerr << "+:"; + } else { + cerr << "-:"; + } + cerr << wid << "] is selecting before vector." << endl; + } + if (rel_base + wid > reg->vector_width()) { + cerr << get_fileline() << ": warning: " << reg->name(); + if (reg->array_dimensions() > 0) cerr << "[]"; + cerr << "[" << lsv; + if (use_sel == index_component_t::SEL_IDX_UP) { + cerr << "+:"; + } else { + cerr << "-:"; + } + cerr << wid << "] is selecting after vector." << endl; + } + } + } else { + if (warn_ob_select) { + cerr << get_fileline() << ": warning: " << reg->name(); + if (reg->array_dimensions() > 0) cerr << "[]"; + cerr << "['bx"; + if (use_sel == index_component_t::SEL_IDX_UP) { + cerr << "+:"; + } else { + cerr << "-:"; + } + cerr << wid << "] is always outside vector." << endl; + } + } + } else { + /* Correct the mux for the range of the vector. */ + if (use_sel == index_component_t::SEL_IDX_UP) { + if (reg->msb() > reg->lsb()) { + if (long offset = reg->lsb()) + base = make_add_expr(base, -offset); + } else { + base = make_sub_expr(reg->lsb()-wid+1, base); + } + } else { + // This is assumed to be a SEL_IDX_DO. + if (reg->msb() > reg->lsb()) { + if (long offset = reg->lsb()+wid-1) + base = make_add_expr(base, -offset); + } else { + base = make_sub_expr(reg->lsb(), base); + } + } } if (debug_elaborate) diff --git a/elab_net.cc b/elab_net.cc index 6daa358d2..510f78a1d 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -238,15 +238,33 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, return 0; } - long midx_val = tmp->value().as_long(); - midx = sig->sb_to_idx(midx_val); - delete tmp_ex; - /* The width (a constant) is calculated here. */ unsigned long wid = 0; bool flag = calculate_up_do_width_(des, scope, wid); - if (! flag) + if (! flag) return false; + + /* We have an undefined index and that is out of range. */ + if (! tmp->value().is_defined()) { + if (warn_ob_select) { + cerr << get_fileline() << ": warning: " + << sig->name(); + if (sig->array_dimensions() > 0) cerr << "[]"; + cerr << "['bx"; + if (index_tail.sel == + index_component_t::SEL_IDX_UP) { + cerr << "+:"; + } else { + cerr << "-:"; + } + cerr << wid << "] is always outside vector." + << endl; + } return false; + } + + long midx_val = tmp->value().as_long(); + midx = sig->sb_to_idx(midx_val); + delete tmp_ex; if (index_tail.sel == index_component_t::SEL_IDX_UP) lidx = sig->sb_to_idx(midx_val+wid-1); @@ -260,9 +278,19 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, } /* Warn about an indexed part select that is out of range. */ - if (midx >= (long)sig->vector_width() || lidx < 0) { - cerr << get_fileline() << ": warning: Indexed part " - "select " << sig->name(); + if (warn_ob_select && (lidx < 0)) { + cerr << get_fileline() << ": warning: " << sig->name(); + if (sig->array_dimensions() > 0) cerr << "[]"; + cerr << "[" << midx_val; + if (index_tail.sel == index_component_t::SEL_IDX_UP) { + cerr << "+:"; + } else { + cerr << "-:"; + } + cerr << wid << "] is selecting before vector." << endl; + } + if (warn_ob_select && (midx >= (long)sig->vector_width())) { + cerr << get_fileline() << ": warning: " << sig->name(); if (sig->array_dimensions() > 0) { cerr << "[]"; } @@ -272,7 +300,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, } else { cerr << "-:"; } - cerr << wid << "] is out of range." << endl; + cerr << wid << "] is selecting after vector." << endl; } /* This is completely out side the signal so just skip it. */ diff --git a/netlist.cc b/netlist.cc index 8f2940316..095338ff3 100644 --- a/netlist.cc +++ b/netlist.cc @@ -914,9 +914,8 @@ NetPartSelect::NetPartSelect(NetNet*sig, NetNet*sel, pin(1).set_dir(Link::INPUT); break; case NetPartSelect::PV: - pin(0).set_dir(Link::INPUT); - pin(1).set_dir(Link::OUTPUT); - break; + /* Only a vector to part can be a variable select. */ + assert(0); } pin(2).set_dir(Link::INPUT); @@ -2349,12 +2348,12 @@ NetNet* NetESignal::sig() return net_; } -unsigned NetESignal::lsi() const +long NetESignal::lsi() const { return net_->lsb(); } -unsigned NetESignal::msi() const +long NetESignal::msi() const { return net_->msb(); } diff --git a/netlist.h b/netlist.h index d34fca4c5..9162e7b71 100644 --- a/netlist.h +++ b/netlist.h @@ -3829,8 +3829,8 @@ class NetESignal : public NetExpr { const NetNet* sig() const; NetNet* sig(); // Declared vector dimensions for the signal. - unsigned msi() const; - unsigned lsi() const; + long msi() const; + long lsi() const; virtual ivl_variable_type_t expr_type() const; diff --git a/tgt-vvp/draw_vpi.c b/tgt-vvp/draw_vpi.c index 5ab4e9d8f..0858e672c 100644 --- a/tgt-vvp/draw_vpi.c +++ b/tgt-vvp/draw_vpi.c @@ -109,6 +109,7 @@ static int get_vpi_taskfunc_signal_arg(struct args_info *result, if (word_ex) { /* Some array select have been evaluated. */ if (number_is_immediate(word_ex,IMM_WID, 0)) { + assert(! number_is_unknown(word_ex)); use_word = get_number_immediate(word_ex); word_ex = 0; } @@ -130,6 +131,7 @@ static int get_vpi_taskfunc_signal_arg(struct args_info *result, if (word_ex) { /* Some array select have been evaluated. */ if (number_is_immediate(word_ex, IMM_WID, 0)) { + assert(! number_is_unknown(word_ex)); use_word = get_number_immediate(word_ex); use_word_defined = 1; word_ex = 0; @@ -185,6 +187,7 @@ static int get_vpi_taskfunc_signal_arg(struct args_info *result, /* This is a constant bit/part select. */ if (number_is_immediate(bexpr, 64, 1)) { + assert(! number_is_unknown(bexpr)); snprintf(buffer, sizeof buffer, "&PV", ivl_expr_signal(vexpr), get_number_immediate(bexpr), @@ -206,9 +209,19 @@ static int get_vpi_taskfunc_signal_arg(struct args_info *result, return 0; } } else { - /* Fallback case: evaluate the expression. */ + /* Fallback case: evaluate the expression. */ struct vector_info rv; rv = draw_eval_expr(bexpr, STUFF_OK_XZ); + /* We need to enhance &PV<> to support a signed index. */ + if (ivl_expr_signed(bexpr) && + (ivl_expr_width(bexpr) < 8*sizeof(int))) { + fprintf(stderr, "%s:%u: tgt-vvp warning: V0.9 may give " + "incorrect results for a select with a " + "signed index less than %d bits.\n", + ivl_expr_file(expr), + ivl_expr_lineno(expr), + 8*sizeof(int)); + } snprintf(buffer, sizeof buffer, "&PV", ivl_expr_signal(vexpr), rv.base, rv.wid, diff --git a/tgt-vvp/eval_expr.c b/tgt-vvp/eval_expr.c index 3e6074794..a41ba97f9 100644 --- a/tgt-vvp/eval_expr.c +++ b/tgt-vvp/eval_expr.c @@ -174,6 +174,12 @@ static void eval_logic_into_integer(ivl_expr_t expr, unsigned ix) case IVL_EX_ULONG: { assert(number_is_immediate(expr, IMM_WID, 1)); + if (number_is_unknown(expr)) { + /* We are loading a 'bx so mimic %ix/get. */ + fprintf(vvp_out, " %%ix/load %u, 0, 0;\n", ix); + fprintf(vvp_out, " %%mov 4, 1, 1;\n"); + break; + } long imm = get_number_immediate(expr); if (imm >= 0) { fprintf(vvp_out, " %%ix/load %u, %ld, 0;\n", ix, imm); @@ -192,6 +198,7 @@ static void eval_logic_into_integer(ivl_expr_t expr, unsigned ix) unsigned word = 0; if (ivl_signal_dimensions(sig) > 0) { ivl_expr_t ixe; + char*type = ivl_expr_signed(expr) ? "/s" : ""; /* Detect the special case that this is a variable array. In this case, the ix/getv @@ -199,20 +206,21 @@ static void eval_logic_into_integer(ivl_expr_t expr, unsigned ix) if (ivl_signal_type(sig) == IVL_SIT_REG) { struct vector_info rv; rv = draw_eval_expr(expr, 0); - fprintf(vvp_out, " %%ix/get %u, %u, %u;\n", - ix, rv.base, rv.wid); + fprintf(vvp_out, " %%ix/get%s %u, %u, %u;\n", + type, ix, rv.base, rv.wid); clr_vector(rv); break; } ixe = ivl_expr_oper1(expr); - if (number_is_immediate(ixe, IMM_WID, 0)) + if (number_is_immediate(ixe, IMM_WID, 0)) { + assert(! number_is_unknown(ixe)); word = get_number_immediate(ixe); - else { + } else { struct vector_info rv; rv = draw_eval_expr(expr, 0); - fprintf(vvp_out, " %%ix/get %u, %u, %u;\n", - ix, rv.base, rv.wid); + fprintf(vvp_out, " %%ix/get%s %u, %u, %u;\n", + type, ix, rv.base, rv.wid); clr_vector(rv); break; } @@ -226,8 +234,14 @@ static void eval_logic_into_integer(ivl_expr_t expr, unsigned ix) default: { struct vector_info rv; rv = draw_eval_expr(expr, 0); - fprintf(vvp_out, " %%ix/get %u, %u, %u;\n", - ix, rv.base, rv.wid); + /* Is this a signed expression? */ + if (ivl_expr_signed(expr)) { + fprintf(vvp_out, " %%ix/get/s %u, %u, %u;\n", + ix, rv.base, rv.wid); + } else { + fprintf(vvp_out, " %%ix/get %u, %u, %u;\n", + ix, rv.base, rv.wid); + } clr_vector(rv); break; } @@ -274,7 +288,10 @@ static struct vector_info draw_eq_immediate(ivl_expr_t exp, unsigned ewid, { unsigned wid; struct vector_info lv; - unsigned long imm = get_number_immediate(re); + unsigned long imm; + assert(number_is_immediate(re, IMM_WID, 0)); + assert(! number_is_unknown(re)); + imm = get_number_immediate(re); wid = ivl_expr_width(le); lv = draw_eval_expr_wid(le, wid, stuff_ok_flag); @@ -991,6 +1008,8 @@ static struct vector_info draw_logic_immediate(ivl_expr_t exp, unsigned wid) { struct vector_info lv = draw_eval_expr_wid(le, wid, STUFF_OK_XZ); + assert(number_is_immediate(re, IMM_WID, 0)); + assert(! number_is_unknown(re)); unsigned long imm = get_number_immediate(re); assert(lv.base >= 4); @@ -1241,8 +1260,10 @@ static struct vector_info draw_load_add_immediate(ivl_expr_t le, int signed_flag) { struct vector_info lv; - long imm = get_number_immediate(re); + long imm; + assert(! number_is_unknown(re)); assert(number_is_immediate(re, IMM_WID, 1)); + imm = get_number_immediate(re); lv.base = allocate_vector(wid); lv.wid = wid; if (lv.base == 0) { @@ -1270,6 +1291,8 @@ static struct vector_info draw_add_immediate(ivl_expr_t le, lv = draw_eval_expr_wid(le, wid, STUFF_OK_XZ); assert(lv.wid == wid); + assert(! number_is_unknown(re)); + assert(number_is_immediate(re, IMM_WID, 0)); imm = get_number_immediate(re); /* This shouldn't generally happen, because the elaborator @@ -1308,7 +1331,7 @@ static struct vector_info draw_add_immediate(ivl_expr_t le, } break; - case 2: /* Left expression is X or Z */ + case 2: /* Left expression is 'bx or 'bz */ case 3: lv.base = 2; break; @@ -1335,6 +1358,8 @@ static struct vector_info draw_sub_immediate(ivl_expr_t le, lv = draw_eval_expr_wid(le, wid, STUFF_OK_XZ); assert(lv.wid == wid); + assert(! number_is_unknown(re)); + assert(number_is_immediate(re, IMM_WID, 0)); imm = get_number_immediate(re); if (imm == 0) return lv; @@ -1379,6 +1404,8 @@ static struct vector_info draw_mul_immediate(ivl_expr_t le, lv = draw_eval_expr_wid(le, wid, STUFF_OK_XZ); assert(lv.wid == wid); + assert(! number_is_unknown(re)); + assert(number_is_immediate(re, IMM_WID, 0)); imm = get_number_immediate(re); if (imm == 0) return lv; @@ -2253,7 +2280,7 @@ static struct vector_info draw_select_array(ivl_expr_t sube, unsigned bit_width, unsigned wid) { - unsigned idx; + unsigned idx, label; ivl_signal_t sig = ivl_expr_signal(sube); /* unsigned sig_wid = ivl_expr_width(sube); */ ivl_expr_t ix = ivl_expr_oper1(sube); @@ -2263,7 +2290,18 @@ static struct vector_info draw_select_array(ivl_expr_t sube, shiv = draw_eval_expr(bit_idx, STUFF_OK_XZ|STUFF_OK_RO); draw_eval_expr_into_integer(ix, 3); - fprintf(vvp_out, " %%ix/get 0, %u, %u;\n", shiv.base, shiv.wid); + label = local_count++; + /* We can safely skip the bit index load below if the array word + * index is undefined. We need to do this so that the bit index + * load does not reset bit 4 to zero by loading a defined value. */ + fprintf(vvp_out, " %%jmp/1 T_%d.%d, 4;\n", thread_count, label); + if (ivl_expr_signed(bit_idx)) { + fprintf(vvp_out, " %%ix/get/s 0, %u, %u;\n", shiv.base, + shiv.wid); + } else { + fprintf(vvp_out, " %%ix/get 0, %u, %u;\n", shiv.base, shiv.wid); + } + fprintf(vvp_out, "T_%d.%d ;\n", thread_count, label); if (shiv.base >= 8) clr_vector(shiv); @@ -2294,7 +2332,7 @@ static struct vector_info draw_select_signal(ivl_expr_t sube, /* Use this word of the signal. */ unsigned use_word = 0; - unsigned use_wid; + unsigned use_wid, lab_x, lab_end; /* If this is an access to an array, try to get the index as a constant. If it is (and the array is not a reg array then @@ -2311,6 +2349,7 @@ static struct vector_info draw_select_signal(ivl_expr_t sube, /* The index is constant, so we can return to direct readout with the specific word selected. */ + assert(! number_is_unknown(ix)); use_word = get_number_immediate(ix); } @@ -2318,6 +2357,7 @@ static struct vector_info draw_select_signal(ivl_expr_t sube, the signal (or the entire width). Just load the early bits in one go. */ if (number_is_immediate(bit_idx, 32, 0) + && !number_is_unknown(bit_idx) && get_number_immediate(bit_idx) == 0 && (ivl_expr_width(sube) >= bit_wid)) { @@ -2343,6 +2383,12 @@ static struct vector_info draw_select_signal(ivl_expr_t sube, res.wid = wid; assert(res.base); + lab_x = local_count++; + lab_end = local_count++; + + /* If the index is 'bx then we just return 'bx. */ + fprintf(vvp_out, " %%jmp/1 T_%d.%d, 4;\n", thread_count, lab_x); + use_wid = res.wid; if (use_wid > bit_wid) use_wid = bit_wid; @@ -2353,6 +2399,11 @@ static struct vector_info draw_select_signal(ivl_expr_t sube, fprintf(vvp_out, " %%movi %u, 0, %u;\n", res.base + use_wid, res.wid - use_wid); + fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count, lab_end); + fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_x); + fprintf(vvp_out, " %%mov %u, 2, %u;\n", res.base, res.wid); + fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_end); + return res; } @@ -2371,6 +2422,7 @@ static void draw_select_signal_dest(ivl_expr_t sube, if ((ivl_signal_dimensions(sig) == 0) && (ivl_expr_width(sube) >= dest.wid) && number_is_immediate(bit_idx, 32, 0) + && ! number_is_unknown(bit_idx) && get_number_immediate(bit_idx) == 0) { unsigned use_word = 0; fprintf(vvp_out, " %%load/v %u, v%p_%u, %u; Select %u out of %u bits\n", @@ -2401,6 +2453,8 @@ static struct vector_info draw_select_expr(ivl_expr_t exp, unsigned wid, ivl_expr_t shift = ivl_expr_oper2(exp); int alloc_exclusive = (stuff_ok_flag&STUFF_OK_RO)? 0 : 1; + int cmp; + unsigned lab_l, lab_end; res.wid = wid; @@ -2444,7 +2498,12 @@ static struct vector_info draw_select_expr(ivl_expr_t exp, unsigned wid, return res; } - fprintf(vvp_out, " %%ix/get 0, %u, %u;\n", shiv.base, shiv.wid); + if (ivl_expr_signed(shift)) { + fprintf(vvp_out, " %%ix/get/s 0, %u, %u;\n", shiv.base, + shiv.wid); + } else { + fprintf(vvp_out, " %%ix/get 0, %u, %u;\n", shiv.base, shiv.wid); + } clr_vector(shiv); /* If the subv result is a magic constant, then make a copy in @@ -2458,7 +2517,27 @@ static struct vector_info draw_select_expr(ivl_expr_t exp, unsigned wid, subv = res; } + lab_l = local_count++; + lab_end = local_count++; + /* If we have an undefined index then just produce a 'bx result. */ + fprintf(vvp_out, " %%jmp/1 T_%d.%d, 4;\n", thread_count, lab_l); + + cmp = allocate_word(); + assert(subv.wid >= wid); + /* Determine if we need to shift a 'bx into the top. */ + fprintf(vvp_out, " %%ix/load %u, %u, 0;\n", cmp, subv.wid - wid); + fprintf(vvp_out, " %%cmp/ws %u, 0;\n", cmp); + clr_word(cmp); + fprintf(vvp_out, " %%jmp/1 T_%d.%d, 5;\n", thread_count, lab_l); + /* Clear the cmp bit if the two values are equal. */ + fprintf(vvp_out, " %%mov 4, 0, 1;\n"); fprintf(vvp_out, " %%shiftr/i0 %u, %u;\n", subv.base, subv.wid); + fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count, lab_end); + fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_l); + /* Multiply by -1. */ + fprintf(vvp_out, " %%ix/mul 0, %u, %u;\n", 0xFFFFFFFF, 0xFFFFFFFF); + fprintf(vvp_out, " %%shiftl/i0 %u, %u;\n", subv.base, subv.wid); + fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_end); if (subv.wid > wid) { res.base = subv.base; diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index d486edd46..14c25097a 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -558,6 +558,9 @@ static void draw_delay(ivl_net_logic_t lptr) assert(number_is_immediate(d0, 64, 0)); assert(number_is_immediate(d1, 64, 0)); assert(number_is_immediate(d2, 64, 0)); + assert(! number_is_unknown(d0)); + assert(! number_is_unknown(d1)); + assert(! number_is_unknown(d2)); if (d0 == d1 && d1 == d2) fprintf(vvp_out, " (%lu)", get_number_immediate(d0)); @@ -874,6 +877,9 @@ static void draw_logic_in_scope(ivl_net_logic_t lptr) && number_is_immediate(fall_exp,64,0) && number_is_immediate(decay_exp,64,0)) { + assert(! number_is_unknown(rise_exp)); + assert(! number_is_unknown(fall_exp)); + assert(! number_is_unknown(decay_exp)); fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n", lptr, get_number_immediate(rise_exp), get_number_immediate(fall_exp), @@ -1079,6 +1085,9 @@ static const char* draw_lpm_output_delay(ivl_lpm_t net) assert(number_is_immediate(d_rise, 64, 0)); assert(number_is_immediate(d_fall, 64, 0)); assert(number_is_immediate(d_decay, 64, 0)); + assert(! number_is_unknown(d_rise)); + assert(! number_is_unknown(d_fall)); + assert(! number_is_unknown(d_decay)); dly = "/d"; fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n", net, get_number_immediate(d_rise),