Logo Search packages:      
Sourcecode: iverilog version File versions  Download package

elab_lval.cc

/*
 * Copyright (c) 2000-2009 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
 *    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 "config.h"

# include  "PExpr.h"
# include  "netlist.h"
# include  "netmisc.h"
# include  "compiler.h"
# include  <cstdlib>
# include  <iostream>
# include  <climits>
# include  "ivl_assert.h"

/*
 * These methods generate a NetAssign_ object for the l-value of the
 * assignment. This is common code for the = and <= statements.
 *
 * What gets generated depends on the structure of the l-value. If the
 * l-value is a simple name (i.e., foo <= <value>) then the NetAssign_
 * is created the width of the foo reg and connected to all the
 * bits.
 *
 * If there is a part select (i.e., foo[3:1] <= <value>) the NetAssign_
 * is made only as wide as it needs to be (3 bits in this example) and
 * connected to the correct bits of foo. A constant bit select is a
 * special case of the part select.
 *
 * If the bit-select is non-constant (i.e., foo[<expr>] = <value>) the
 * NetAssign_ is made wide enough to connect to all the bits of foo,
 * then the mux expression is elaborated and attached to the
 * NetAssign_ node as a b_mux value. The target must interpret the
 * presence of a bmux value as taking a single bit and assigning it to
 * the bit selected by the bmux expression.
 *
 * If the l-value expression is non-trivial, but can be fully
 * evaluated at compile time (meaning any bit selects are constant)
 * then elaboration will make a single NetAssign_ that connects to a
 * synthetic reg that in turn connects to all the proper pins of the
 * l-value.
 *
 * This last case can turn up in statements like: {a, b[1]} = c;
 * rather then create a NetAssign_ for each item in the concatenation,
 * elaboration makes a single NetAssign_ and connects it up properly.
 */


/*
 * The default interpretation of an l-value to a procedural assignment
 * is to try to make a net elaboration, and see if the result is
 * suitable for assignment.
 */
NetAssign_* PExpr::elaborate_lval(Design*des,
                          NetScope*scope,
                          bool is_force) const
{
      NetNet*ll = 0;
      if (ll == 0) {
          cerr << get_fileline() << ": Assignment l-value too complex."
             << endl;
          return 0;
      }

      NetAssign_*lv = new NetAssign_(ll);
      return lv;
}

/*
 * Concatenation expressions can appear as l-values. Handle them here.
 *
 * If adjacent l-values in the concatenation are not bit selects, then
 * merge them into a single NetAssign_ object. This can happen is code
 * like ``{ ...a, b, ...}''. As long as "a" and "b" do not have bit
 * selects (or the bit selects are constant) we can merge the
 * NetAssign_ objects.
 *
 * Be careful to get the bit order right. In the expression ``{a, b}''
 * a is the MSB and b the LSB. Connect the LSB to the low pins of the
 * NetAssign_ object.
 */
NetAssign_* PEConcat::elaborate_lval(Design*des,
                             NetScope*scope,
                             bool is_force) const
{
      if (repeat_) {
          cerr << get_fileline() << ": error: Repeat concatenations make "
              "no sense in l-value expressions. I refuse." << endl;
          des->errors += 1;
          return 0;
      }

      NetAssign_*res = 0;

      for (unsigned idx = 0 ;  idx < parms_.count() ;  idx += 1) {

          if (parms_[idx] == 0) {
              cerr << get_fileline() << ": error: Empty expressions "
                   << "not allowed in concatenations." << endl;
              des->errors += 1;
              continue;
          }

          NetAssign_*tmp = parms_[idx]->elaborate_lval(des, scope, is_force);

            /* If the l-value doesn't elaborate, the error was
             already detected and printed. We just skip it and let
             the compiler catch more errors. */
          if (tmp == 0)
              continue;

          assert(tmp);


            /* Link the new l-value to the previous one. */

          NetAssign_*last = tmp;
          while (last->more)
              last = last->more;

          last->more = res;
          res = tmp;
      }

      return res;
}

/*
 * Handle the ident as an l-value. This includes bit and part selects
 * of that ident.
 */
NetAssign_* PEIdent::elaborate_lval(Design*des,
                            NetScope*scope,
                            bool is_force) const
{
      NetNet*       reg = 0;
      const NetExpr*par = 0;
      NetEvent*     eve = 0;

      symbol_search(this, des, scope, path_, reg, par, eve);
      if (reg == 0) {
          cerr << get_fileline() << ": error: Could not find variable ``"
             << path_ << "'' in ``" << scope_path(scope) <<
              "''" << endl;

          des->errors += 1;
          return 0;
      }

      ivl_assert(*this, reg);

      const name_component_t&name_tail = path_.back();

      index_component_t::ctype_t use_sel = index_component_t::SEL_NONE;
      if (!name_tail.index.empty())
          use_sel = name_tail.index.back().sel;

      // This is the special case that the l-value is an entire
      // memory. This is, in fact, an error.
      if (reg->array_dimensions() > 0 && name_tail.index.empty()) {
          cerr << get_fileline() << ": error: Cannot assign to array "
             << path_ << ". Did you forget a word index?" << endl;
          des->errors += 1;
          return 0;
      }

      /* Get the signal referenced by the identifier, and make sure
         it is a register. Wires are not allows in this context,
         unless this is the l-value of a force. */
      if ((reg->type() != NetNet::REG) && !is_force) {
          cerr << get_fileline() << ": error: " << path_ <<
              " is not a valid l-value in " << scope_path(scope) <<
              "." << endl;
          cerr << reg->get_fileline() << ":      : " << path_ <<
              " is declared here as " << reg->type() << "." << endl;
          des->errors += 1;
          return 0;
      }

      if (reg->array_dimensions() > 0)
          return elaborate_lval_net_word_(des, scope, reg);

      if (use_sel == index_component_t::SEL_PART) {
          NetAssign_*lv = new NetAssign_(reg);
          elaborate_lval_net_part_(des, scope, lv);
          return lv;
      }

      if (use_sel == index_component_t::SEL_IDX_UP ||
          use_sel == index_component_t::SEL_IDX_DO) {
          NetAssign_*lv = new NetAssign_(reg);
          elaborate_lval_net_idx_(des, scope, lv, use_sel);
          return lv;
      }


      if (use_sel == index_component_t::SEL_BIT) {
          NetAssign_*lv = new NetAssign_(reg);
          elaborate_lval_net_bit_(des, scope, lv);
          return lv;
      }

      ivl_assert(*this, use_sel == index_component_t::SEL_NONE);

      /* No select expressions. */

      NetAssign_*lv = new NetAssign_(reg);

      return lv;
}

NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des,
                                    NetScope*scope,
                                    NetNet*reg) const
{
      const name_component_t&name_tail = path_.back();
      ivl_assert(*this, !name_tail.index.empty());

      const index_component_t&index_head = name_tail.index.front();
      if (index_head.sel == index_component_t::SEL_PART) {
          cerr << get_fileline() << ": error: cannot perform a part "
               << "select on array " << reg->name() << "." << endl;
          des->errors += 1;
          return 0;
      }

      ivl_assert(*this, index_head.sel == index_component_t::SEL_BIT);
      ivl_assert(*this, index_head.msb != 0);
      ivl_assert(*this, index_head.lsb == 0);

      ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE;
      // This not used, but it needs to have a default value.
      bool unsized_flag_tmp = false;
      index_head.msb->test_width(des, scope,
                       reg->vector_width(), reg->vector_width(),
                       expr_type_tmp,
                       unsized_flag_tmp);

      NetExpr*word = elab_and_eval(des, scope, index_head.msb, -1);

      // If there is a non-zero base to the memory, then build an
      // expression to calculate the canonical address.
      if (long base = reg->array_first()) {

          word = make_add_expr(word, 0-base);
          eval_expr(word);
      }

      NetAssign_*lv = new NetAssign_(reg);
      lv->set_word(word);

      if (debug_elaborate)
          cerr << get_fileline() << ": debug: Set array word=" << *word << endl;

      // Test for the case that the index is a constant, and is out
      // of bounds. The "word" expression is the word index already
      // converted to canonical address, so this just needs to check
      // that the address is not too big.
      if (NetEConst*word_const = dynamic_cast<NetEConst*>(word)) {
          verinum word_val = word_const->value();
          long index = word_val.as_long();

          if (index < 0 || index >= (long) reg->array_count()) {
              cerr << get_fileline() << ": warning: Constant array index "
                   << (index + reg->array_first())
                   << " is out of range for array "
                   << reg->name() << "." << endl;
          }
      }

      /* An array word may also have part selects applied to them. */

      index_component_t::ctype_t use_sel = index_component_t::SEL_NONE;
      if (name_tail.index.size() > 1)
          use_sel = name_tail.index.back().sel;

      if (use_sel == index_component_t::SEL_PART)
          elaborate_lval_net_part_(des, scope, lv);

      if (use_sel == index_component_t::SEL_IDX_UP ||
          use_sel == index_component_t::SEL_IDX_DO)
          elaborate_lval_net_idx_(des, scope, lv, use_sel);

      return lv;
}

bool PEIdent::elaborate_lval_net_bit_(Design*des,
                              NetScope*scope,
                              NetAssign_*lv) const
{
      const name_component_t&name_tail = path_.back();
      const index_component_t&index_tail = name_tail.index.back();
      ivl_assert(*this, index_tail.msb != 0);
      ivl_assert(*this, index_tail.lsb == 0);

      NetNet*reg = lv->sig();

      ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE;
      // This not used, but it needs to have a default value.
      bool unsized_flag_tmp = false;
      index_tail.msb->test_width(des, scope,
                       lv->lwidth(), lv->lwidth(),
                       expr_type_tmp,
                       unsized_flag_tmp);

    
    // Bit selects have a single select expression. Evaluate the
      // constant value and treat it as a part select with a bit
      // width of 1.
      NetExpr*mux = elab_and_eval(des, scope, index_tail.msb, -1);
      long lsb = 0;

      if (NetEConst*index_con = dynamic_cast<NetEConst*> (mux)) {
          lsb = index_con->value().as_long();
          mux = 0;
      }

      if (mux) {
            // Non-constant bit mux. Correct the mux for the range
            // of the vector, then set the l-value part select expression.
          if (reg->msb() < reg->lsb())
              mux = make_sub_expr(reg->lsb(), mux);
          else if (reg->lsb() != 0)
              mux = make_add_expr(mux, - reg->lsb());

          lv->set_part(mux, 1);

      } else if (lsb == reg->msb() && lsb == reg->lsb()) {
            // Constant bit mux that happens to select the only bit
            // of the l-value. Don't bother with any select at all.

      } else {
            // Constant bit select that does something useful.
          long loff = reg->sb_to_idx(lsb);

          if (loff < 0 || loff >= (long)reg->vector_width()) {
              cerr << get_fileline() << ": error: bit select "
                   << reg->name() << "[" <<lsb<<"]"
                   << " is out of range." << endl;
              des->errors += 1;
              return 0;
          }

          lv->set_part(new NetEConst(verinum(loff)), 1);
      }

      return true;
}

bool PEIdent::elaborate_lval_net_part_(Design*des,
                               NetScope*scope,
                               NetAssign_*lv) const
{
      // The range expressions of a part select must be
      // constant. The calculate_parts_ function calculates the
      // values into msb and lsb.
      long msb, lsb;
      bool parts_defined_flag;
      bool flag = calculate_parts_(des, scope, msb, lsb, parts_defined_flag);
      if (!flag)
          return false;

      ivl_assert(*this, parts_defined_flag);

      NetNet*reg = lv->sig();
      assert(reg);

      if (msb == reg->msb() && lsb == reg->lsb()) {

            /* Part select covers the entire vector. Simplest case. */

      } else {

            /* Get the canonical offsets into the vector. */
          long loff = reg->sb_to_idx(lsb);
          long moff = reg->sb_to_idx(msb);
          long wid = moff - loff + 1;

          if (moff < loff) {
              cerr << get_fileline() << ": error: part select "
                   << reg->name() << "[" << msb<<":"<<lsb<<"]"
                   << " is reversed." << endl;
              des->errors += 1;
              return false;
          }

            /* If the part select extends beyond the extremes of the
             variable, then report an error. Note that loff is
             converted to normalized form so is relative the
             variable pins. */

          if (loff < 0 || moff >= (signed)reg->vector_width()) {
              cerr << get_fileline() << ": warning: Part select "
                   << reg->name() << "[" << msb<<":"<<lsb<<"]"
                   << " is out of range." << endl;
          }

          lv->set_part(new NetEConst(verinum(loff)), wid);
      }

      return true;
}

bool PEIdent::elaborate_lval_net_idx_(Design*des,
                              NetScope*scope,
                              NetAssign_*lv,
                              index_component_t::ctype_t use_sel) const
{
      const name_component_t&name_tail = path_.back();;
      ivl_assert(*this, !name_tail.index.empty());

      const index_component_t&index_tail = name_tail.index.back();
      ivl_assert(*this, index_tail.msb != 0);
      ivl_assert(*this, index_tail.lsb != 0);

      NetNet*reg = lv->sig();
      assert(reg);

      if (reg->type() != NetNet::REG) {
          cerr << get_fileline() << ": error: " << path_ <<
              " is not a reg/integer/time in " << scope_path(scope) <<
              "." << endl;
          cerr << reg->get_fileline() << ":      : " << path_ <<
              " is declared here as " << reg->type() << "." << endl;
          des->errors += 1;
          return false;
      }

      unsigned long wid;
      calculate_up_do_width_(des, scope, wid);

      ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE;
      // This not used, but it needs to have a default value.
      bool unsized_flag_tmp = false;
      index_tail.msb->test_width(des, scope,
                       wid, wid,
                       expr_type_tmp,
                       unsized_flag_tmp);

      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);
      }

      if (debug_elaborate)
          cerr << get_fileline() << ": debug: Set part select width="
             << wid << ", base=" << *base << endl;

      lv->set_part(base, wid);

      return true;
}

NetAssign_* PENumber::elaborate_lval(Design*des, NetScope*, bool) const
{
      cerr << get_fileline() << ": error: Constant values not allowed "
         << "in l-value expressions." << endl;
      des->errors += 1;
      return 0;
}

Generated by  Doxygen 1.6.0   Back to index