From 312b4da46fe94a2af619b3c95f0503334bf19a4e Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Sat, 26 Feb 2011 22:59:52 +0000 Subject: [PATCH] Expression width rework. This patch is a major rework of expression elaboration and evaluation in the compiler, aimed at better compliance with the IEEE standard. --- Makefile.in | 2 +- PDelays.cc | 8 +- PExpr.cc | 25 +- PExpr.h | 260 +++--- design_dump.cc | 8 +- dup_expr.cc | 146 +++- elab_expr.cc | 1865 ++++++++++++++++++------------------------- elab_lval.cc | 20 +- elab_net.cc | 12 - elab_scope.cc | 18 +- elab_sig.cc | 13 +- elaborate.cc | 169 +--- elaborate_analog.cc | 4 +- eval_tree.cc | 318 +++----- net_design.cc | 41 +- net_expr.cc | 276 +------ net_proc.cc | 5 +- netlist.cc | 124 +-- netlist.h | 146 +--- netmisc.cc | 143 +++- netmisc.h | 47 +- set_width.cc | 486 ----------- t-dll-expr.cc | 4 +- verinum.cc | 37 +- verinum.h | 9 +- 25 files changed, 1500 insertions(+), 2686 deletions(-) delete mode 100644 set_width.cc diff --git a/Makefile.in b/Makefile.in index 33d264a54..8704bfc8f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -106,7 +106,7 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ netenum.o net_event.o net_expr.o net_func.o net_link.o net_modulo.o \ net_nex_input.o net_nex_output.o net_proc.o net_scope.o net_tran.o \ net_udp.o pad_to_width.o parse.o parse_misc.o pform.o pform_analog.o \ - pform_disciplines.o pform_dump.o pform_types.o set_width.o \ + pform_disciplines.o pform_dump.o pform_types.o \ symbol_search.o sync.o sys_funcs.o verinum.o verireal.o target.o \ Attrib.o HName.o Module.o PDelays.o PEvent.o PExpr.o PGate.o \ PGenerate.o PScope.o PSpec.o PTask.o PUdp.o PFunction.o PWire.o \ diff --git a/PDelays.cc b/PDelays.cc index 40ea8b262..8e9ea79fc 100644 --- a/PDelays.cc +++ b/PDelays.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2011 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 @@ -78,11 +78,7 @@ unsigned PDelays::delay_count() const static NetExpr*calculate_val(Design*des, NetScope*scope, PExpr*expr) { - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool tmp_flag = false; - expr->test_width(des, scope, 0, 0, tmp_type, tmp_flag); - NetExpr*dex = expr->elaborate_expr(des, scope, -1, false); - eval_expr(dex); + NetExpr*dex = elab_and_eval(des, scope, expr, -1); /* Print a warning if we find default and `timescale based * delays in the design, since this is likely an error. */ diff --git a/PExpr.cc b/PExpr.cc index cefe23bc7..bd529f174 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2010 Stephen Williams + * Copyright (c) 1998-2011 Stephen Williams * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -31,8 +31,10 @@ PExpr::PExpr() { - expr_width_ = 0; - expr_type_ = IVL_VT_NO_TYPE; + expr_type_ = IVL_VT_NO_TYPE; + expr_width_ = 0; + min_width_ = 0; + signed_flag_ = false; } PExpr::~PExpr() @@ -94,8 +96,8 @@ bool PEBinary::has_aa_term(Design*des, NetScope*scope) const PEBComp::PEBComp(char op, PExpr*l, PExpr*r) : PEBinary(op, l, r) { - left_width_ = 0; - right_width_ = 0; + l_width_ = 0; + r_width_ = 0; } PEBComp::~PEBComp() @@ -204,13 +206,16 @@ bool PECallFunction::has_aa_term(Design*des, NetScope*scope) const } PEConcat::PEConcat(const list&p, PExpr*r) -: parms_(p.size()), tested_widths_(p.size()), repeat_(r) +: parms_(p.size()), width_modes_(p.size()), repeat_(r) { int tmp_idx = 0; assert(parms_.size() == p.size()); for (list::const_iterator idx = p.begin() ; idx != p.end() ; ++idx) parms_[tmp_idx++] = *idx; + + tested_scope_ = 0; + repeat_count_ = 1; } PEConcat::~PEConcat() @@ -437,3 +442,11 @@ bool PEUnary::has_aa_term(Design*des, NetScope*scope) const assert(expr_); return expr_->has_aa_term(des, scope); } + +PEVoid::PEVoid() +{ +} + +PEVoid::~PEVoid() +{ +} diff --git a/PExpr.h b/PExpr.h index 15adfc7f4..15794e6a4 100644 --- a/PExpr.h +++ b/PExpr.h @@ -1,7 +1,7 @@ #ifndef __PExpr_H #define __PExpr_H /* - * Copyright (c) 1998-2010 Stephen Williams + * Copyright (c) 1998-2011 Stephen Williams * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -43,6 +43,8 @@ class NetScope; class PExpr : public LineInfo { public: + enum width_mode_t { SIZED, EXPAND, LOSSLESS, UNSIZED }; + PExpr(); virtual ~PExpr(); @@ -62,53 +64,65 @@ class PExpr : public LineInfo { // references to automatically allocated variables. virtual bool has_aa_term(Design*des, NetScope*scope) const; - // This method tests the width that the expression wants to - // be. It is used by elaboration of assignments to figure out - // the width of the expression. + // This method tests the type and width that the expression wants + // to be. It should be called before elaborating an expression to + // figure out the type and width of the expression. It also figures + // out the minimum width that can be used to evaluate the expression + // without changing the result. This allows the expression width to + // be pruned when not all bits of the result are used. // - // The "min" is the width of the local context, so is the - // minimum width that this function should return. Initially - // this is the same as the lval width. + // Normally mode should be initialised to SIZED before starting to + // test the width of an expression. In SIZED mode the expression + // width will be calculated strictly according to the IEEE standard + // rules for expression width. + // If the expression contains an unsized literal, mode will be + // changed to LOSSLESS. In LOSSLESS mode the expression width will + // be calculated as the minimum width necessary to avoid arithmetic + // overflow or underflow. + // If the expression both contains an unsized literal and contains + // an operation that coerces a vector operand to a different type + // (signed <-> unsigned), mode is changed to UNSIZED. UNSIZED mode + // is the same as LOSSLESS, except that the final expression width + // will be forced to be at least integer_width. This is necessary + // to ensure compatibility with the IEEE standard, which requires + // unsized literals to be treated as having the same width as an + // integer. The lossless width calculation is inadequate in this + // case because coercing an operand to a different type means that + // the expression no longer obeys the normal rules of arithmetic. // - // The "lval" is the width of the destination where this - // result is going to go. This can be used to constrain the - // amount that an expression can reasonably expand. For - // example, there is no point expanding an addition to beyond - // the lval. This extra bit of information allows the - // expression to optimize itself a bit. If the lval==0, then - // the subexpression should not make l-value related - // optimizations. + // If mode is initialised to EXPAND instead of SIZED, the expression + // width will be calculated as the minimum width necessary to avoid + // arithmetic overflow or underflow, even if it contains no unsized + // literals. mode will be changed LOSSLESS or UNSIZED as described + // above. This supports a non-standard mode of expression width + // calculation. // - // The expr_type is an output argument that gives the - // calculated type for the expression. - // - // The unsized_flag is set to true if the expression is - // unsized and therefore expandable. This happens if a - // sub-expression is an unsized literal. Some expressions make - // special use of that. - virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + // When the final value of mode is UNSIZED, the width returned by + // this method is the calculated lossless width, but the width + // returned by a subsequent call to the expr_width method will be + // the final expression width. + virtual unsigned test_width(Design*des, NetScope*scope, + width_mode_t&mode); // After the test_width method is complete, these methods // return valid results. ivl_variable_type_t expr_type() const { return expr_type_; } unsigned expr_width() const { return expr_width_; } + unsigned min_width() const { return min_width_; } + bool has_sign() const { return signed_flag_; } + + // This method allows the expression type (signed/unsigned) + // to be propagated down to any context-dependant operands. + void cast_signed(bool flag) { signed_flag_ = flag; } // Procedural elaboration of the expression. The expr_width is - // the width of the context of the expression (i.e. the - // l-value width of an assignment), - // - // ... or -1 if the expression is self-determined. or - // ... or -2 if the expression is losslessly - // self-determined. This can happen in situations where the - // result is going to a pseudo-infinitely wide context. + // the required width of the expression. // // The sys_task_arg flag is true if expressions are allowed to // be incomplete. virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; // This method elaborates the expression as gates, but // restricted for use as l-values of continuous assignments. @@ -140,9 +154,13 @@ class PExpr : public LineInfo { virtual bool is_the_same(const PExpr*that) const; protected: + unsigned fix_width_(width_mode_t mode); + // The derived class test_width methods should fill these in. ivl_variable_type_t expr_type_; unsigned expr_width_; + unsigned min_width_; + bool signed_flag_; private: // not implemented PExpr(const PExpr&); @@ -165,14 +183,13 @@ class PEConcat : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetNet* elaborate_lnet(Design*des, NetScope*scope) const; virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, bool is_force) const; @@ -181,9 +198,11 @@ class PEConcat : public PExpr { bool bidirectional_flag) const; private: vectorparms_; - std::valarraytested_widths_; + std::valarraywidth_modes_; PExpr*repeat_; + NetScope*tested_scope_; + unsigned repeat_count_; }; /* @@ -232,11 +251,10 @@ class PEFNumber : public PExpr { virtual verinum* eval_const(Design*des, NetScope*sc) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual void dump(ostream&) const; @@ -262,9 +280,7 @@ class PEIdent : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); // Identifiers are allowed (with restrictions) is assign l-values. virtual NetNet* elaborate_lnet(Design*des, NetScope*scope) const; @@ -277,7 +293,8 @@ class PEIdent : public PExpr { bool is_force) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; // Elaborate the PEIdent as a port to a module. This method // only applies to Ident expressions. @@ -326,13 +343,14 @@ class PEIdent : public PExpr { NetScope*found, const NetExpr*par_msb, const NetExpr*par_lsb, - int expr_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_param_part_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found, const NetExpr*par_msb, - const NetExpr*par_lsb) const; + const NetExpr*par_lsb, + unsigned expr_wid) const; NetExpr*elaborate_expr_param_idx_up_(Design*des, NetScope*scope, const NetExpr*par, @@ -349,28 +367,31 @@ class PEIdent : public PExpr { NetScope*scope, NetNet*net, NetScope*found, + unsigned expr_wid, bool sys_task_arg) const; NetExpr*elaborate_expr_net_word_(Design*des, NetScope*scope, NetNet*net, NetScope*found, + unsigned expr_wid, bool sys_task_arg) const; NetExpr*elaborate_expr_net_part_(Design*des, - NetScope*scope, - NetESignal*net, - NetScope*found) const; + NetScope*scope, + NetESignal*net, + NetScope*found, + unsigned expr_wid) const; NetExpr*elaborate_expr_net_idx_up_(Design*des, - NetScope*scope, - NetESignal*net, - NetScope*found) const; + NetScope*scope, + NetESignal*net, + NetScope*found) const; NetExpr*elaborate_expr_net_idx_do_(Design*des, - NetScope*scope, - NetESignal*net, - NetScope*found) const; + NetScope*scope, + NetESignal*net, + NetScope*found) const; NetExpr*elaborate_expr_net_bit_(Design*des, - NetScope*scope, - NetESignal*net, - NetScope*found) const; + NetScope*scope, + NetESignal*net, + NetScope*found) const; private: NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, @@ -390,12 +411,10 @@ class PENumber : public PExpr { virtual void dump(ostream&) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetEConst*elaborate_expr(Design*des, NetScope*, - int expr_width, bool) const; + unsigned expr_wid, bool) const; virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, bool is_force) const; @@ -425,12 +444,10 @@ class PEString : public PExpr { virtual void dump(ostream&) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetEConst*elaborate_expr(Design*des, NetScope*, - int expr_width, bool) const; + unsigned expr_wid, bool) const; verinum* eval_const(Design*, NetScope*) const; private: @@ -450,16 +467,15 @@ class PEUnary : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; private: - NetExpr* elaborate_expr_bits_(NetExpr*operand, int expr_wid) const; + NetExpr* elaborate_expr_bits_(NetExpr*operand, unsigned expr_wid) const; private: char op_; @@ -479,12 +495,11 @@ class PEBinary : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; protected: @@ -493,22 +508,22 @@ class PEBinary : public PExpr { PExpr*right_; NetExpr*elaborate_expr_base_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_eval_expr_base_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_bits_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_div_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_lshift_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_rshift_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_mult_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_add_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; }; @@ -523,16 +538,14 @@ class PEBComp : public PEBinary { ~PEBComp(); virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&flag); + width_mode_t&mode); NetExpr* elaborate_expr(Design*des, NetScope*scope, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, bool sys_task_arg) const; private: - int left_width_; - int right_width_; + unsigned l_width_; + unsigned r_width_; }; /* @@ -545,12 +558,10 @@ class PEBLogic : public PEBinary { ~PEBLogic(); virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&flag); + width_mode_t&mode); NetExpr* elaborate_expr(Design*des, NetScope*scope, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, bool sys_task_arg) const; }; /* @@ -565,17 +576,15 @@ class PEBLeftWidth : public PEBinary { ~PEBLeftWidth() =0; virtual NetExpr*elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const =0; + unsigned expr_wid) const =0; protected: virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, - int expr_width, bool sys_task_arg) const; - + unsigned expr_wid, + bool sys_task_arg) const; }; class PEBPower : public PEBLeftWidth { @@ -585,7 +594,7 @@ class PEBPower : public PEBLeftWidth { ~PEBPower(); NetExpr*elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const; + unsigned expr_wid) const; }; class PEBShift : public PEBLeftWidth { @@ -595,7 +604,7 @@ class PEBShift : public PEBLeftWidth { ~PEBShift(); NetExpr*elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const; + unsigned expr_wid) const; }; /* @@ -615,17 +624,16 @@ class PETernary : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; private: NetExpr* elab_and_eval_alternative_(Design*des, NetScope*scope, - PExpr*expr, int use_wid) const; + PExpr*expr, unsigned expr_wid) const; private: PExpr*expr_; @@ -658,12 +666,11 @@ class PECallFunction : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); private: pform_name_t path_; @@ -671,14 +678,29 @@ class PECallFunction : public PExpr { bool check_call_matches_definition_(Design*des, NetScope*dscope) const; - NetExpr* cast_to_width_(NetExpr*expr, int wid, bool signed_flag) const; + NetExpr* cast_to_width_(NetExpr*expr, unsigned wid) const; - NetExpr* elaborate_sfunc_(Design*des, NetScope*scope, int expr_wid) const; - NetExpr* elaborate_access_func_(Design*des, NetScope*scope, ivl_nature_t) const; + NetExpr* elaborate_sfunc_(Design*des, NetScope*scope, + unsigned expr_wid) const; + NetExpr* elaborate_access_func_(Design*des, NetScope*scope, ivl_nature_t, + unsigned expr_wid) const; unsigned test_width_sfunc_(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); +}; + +/* + * This class is used for error recovery. All methods do nothing and return + * null or default values. + */ +class PEVoid : public PExpr { + + public: + explicit PEVoid(); + ~PEVoid(); + + virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, + unsigned expr_wid, + bool sys_task_arg) const; }; #endif diff --git a/design_dump.cc b/design_dump.cc index 8d02de40d..afdfa5031 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1396,12 +1396,8 @@ void NetEBinary::dump(ostream&o) const void NetEConcat::dump(ostream&o) const { - if (repeat_calculated_) { - if (repeat_value_ != 1) - o << repeat_value_; - } else if (repeat_) { - o << "<" << *repeat_ << ">"; - } + if (repeat_ != 1) + o << repeat_; if (parms_[0]) o << "{" << *parms_[0]; diff --git a/dup_expr.cc b/dup_expr.cc index db7c2bead..d541161e9 100644 --- a/dup_expr.cc +++ b/dup_expr.cc @@ -32,19 +32,103 @@ NetEAccess* NetEAccess::dup_expr() const return tmp; } -NetEBComp* NetEBComp::dup_expr() const +NetEBinary* NetEBinary::dup_expr() const { - NetEBComp*tmp = new NetEBComp(op_, left_->dup_expr(), - right_->dup_expr()); - assert(tmp); + ivl_assert(*this, 0); + return 0; +} + +NetEBAdd* NetEBAdd::dup_expr() const +{ + NetEBAdd*tmp = new NetEBAdd(op_, left_->dup_expr(), right_->dup_expr(), + expr_width(), has_sign()); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } +NetEBBits* NetEBBits::dup_expr() const +{ + NetEBBits*tmp = new NetEBBits(op_, left_->dup_expr(), right_->dup_expr(), + expr_width(), has_sign()); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetEBComp* NetEBComp::dup_expr() const +{ + NetEBComp*tmp = new NetEBComp(op_, left_->dup_expr(), right_->dup_expr()); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetEBDiv* NetEBDiv::dup_expr() const +{ + NetEBDiv*tmp = new NetEBDiv(op_, left_->dup_expr(), right_->dup_expr(), + expr_width(), has_sign()); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetEBLogic* NetEBLogic::dup_expr() const +{ + NetEBLogic*tmp = new NetEBLogic(op_, left_->dup_expr(), right_->dup_expr()); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetEBMult* NetEBMult::dup_expr() const +{ + NetEBMult*tmp = new NetEBMult(op_, left_->dup_expr(), right_->dup_expr(), + expr_width(), has_sign()); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetEBPow* NetEBPow::dup_expr() const +{ + NetEBPow*tmp = new NetEBPow(op_, left_->dup_expr(), right_->dup_expr(), + expr_width(), has_sign()); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetEBShift* NetEBShift::dup_expr() const +{ + NetEBShift*tmp = new NetEBShift(op_, left_->dup_expr(), right_->dup_expr(), + expr_width(), has_sign()); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetEConcat* NetEConcat::dup_expr() const +{ + NetEConcat*dup = new NetEConcat(parms_.count(), repeat_); + ivl_assert(*this, dup); + dup->set_line(*this); + for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) + if (parms_[idx]) { + NetExpr*tmp = parms_[idx]->dup_expr(); + ivl_assert(*this, tmp); + dup->parms_[idx] = tmp; + } + + dup->expr_width(expr_width()); + + return dup; +} + NetEConst* NetEConst::dup_expr() const { NetEConst*tmp = new NetEConst(value_); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -52,7 +136,7 @@ NetEConst* NetEConst::dup_expr() const NetEConstEnum* NetEConstEnum::dup_expr() const { NetEConstEnum*tmp = new NetEConstEnum(scope_, name_, enum_set_, value()); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -60,7 +144,15 @@ NetEConstEnum* NetEConstEnum::dup_expr() const NetEConstParam* NetEConstParam::dup_expr() const { NetEConstParam*tmp = new NetEConstParam(scope_, name_, value()); - assert(tmp); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetECReal* NetECReal::dup_expr() const +{ + NetECReal*tmp = new NetECReal(value_); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -68,26 +160,26 @@ NetEConstParam* NetEConstParam::dup_expr() const NetECRealParam* NetECRealParam::dup_expr() const { NetECRealParam*tmp = new NetECRealParam(scope_, name_, value()); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEEvent* NetEEvent::dup_expr() const { - assert(0); + ivl_assert(*this, 0); return 0; } NetENetenum* NetENetenum::dup_expr() const { - assert(0); + ivl_assert(*this, 0); return 0; } NetEScope* NetEScope::dup_expr() const { - assert(0); + ivl_assert(*this, 0); return 0; } @@ -96,7 +188,7 @@ NetESelect* NetESelect::dup_expr() const NetESelect*tmp = new NetESelect(expr_->dup_expr(), base_? base_->dup_expr() : 0, expr_width(), sel_type_); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -104,11 +196,11 @@ NetESelect* NetESelect::dup_expr() const NetESFunc* NetESFunc::dup_expr() const { NetESFunc*tmp = new NetESFunc(name_, type_, expr_width(), nparms()); - assert(tmp); + ivl_assert(*this, tmp); tmp->cast_signed(has_sign()); for (unsigned idx = 0 ; idx < nparms() ; idx += 1) { - assert(parm(idx)); + ivl_assert(*this, parm(idx)); tmp->parm(idx, parm(idx)->dup_expr()); } @@ -119,7 +211,7 @@ NetESFunc* NetESFunc::dup_expr() const NetESignal* NetESignal::dup_expr() const { NetESignal*tmp = new NetESignal(net_, word_); - assert(tmp); + ivl_assert(*this, tmp); tmp->expr_width(expr_width()); tmp->set_line(*this); return tmp; @@ -129,8 +221,10 @@ NetETernary* NetETernary::dup_expr() const { NetETernary*tmp = new NetETernary(cond_->dup_expr(), true_val_->dup_expr(), - false_val_->dup_expr()); - assert(tmp); + false_val_->dup_expr(), + expr_width(), + has_sign()); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -141,29 +235,29 @@ NetEUFunc* NetEUFunc::dup_expr() const svector tmp_parms (parms_.count()); for (unsigned idx = 0 ; idx < tmp_parms.count() ; idx += 1) { - assert(parms_[idx]); + ivl_assert(*this, parms_[idx]); tmp_parms[idx] = parms_[idx]->dup_expr(); } tmp = new NetEUFunc(scope_, func_, result_sig_->dup_expr(), tmp_parms); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEUBits* NetEUBits::dup_expr() const { - NetEUBits*tmp = new NetEUBits(op_, expr_->dup_expr()); - assert(tmp); + NetEUBits*tmp = new NetEUBits(op_, expr_->dup_expr(), expr_width(), has_sign()); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEUnary* NetEUnary::dup_expr() const { - NetEUnary*tmp = new NetEUnary(op_, expr_->dup_expr()); - assert(tmp); + NetEUnary*tmp = new NetEUnary(op_, expr_->dup_expr(), expr_width(), has_sign()); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -171,15 +265,15 @@ NetEUnary* NetEUnary::dup_expr() const NetEUReduce* NetEUReduce::dup_expr() const { NetEUReduce*tmp = new NetEUReduce(op_, expr_->dup_expr()); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetECast* NetECast::dup_expr() const { - NetECast*tmp = new NetECast(op_, expr_->dup_expr()); - assert(tmp); + NetECast*tmp = new NetECast(op_, expr_->dup_expr(), expr_width(), has_sign()); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } diff --git a/elab_expr.cc b/elab_expr.cc index 878596e4e..b9cf8eec8 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -81,163 +81,140 @@ static NetBranch* find_existing_implicit_branch(NetNet*sig, NetNet*gnd) } NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, - ivl_variable_type_t data_type_lv, int expr_wid_lv, + ivl_variable_type_t lv_type, unsigned lv_width, PExpr*expr) { - bool unsized_flag = type_is_vectorable(data_type_lv)? false : true; - unsigned use_lval_wid = type_is_vectorable(data_type_lv)? expr_wid_lv : 0; - unsigned use_min_wid = expr_wid_lv; - - /* Find out what the r-value width is going to be. We - guess it will be the l-value width, but it may turn - out to be something else based on self-determined - widths inside. */ - ivl_variable_type_t rval_type = IVL_VT_NO_TYPE; - int expr_wid = expr->test_width(des, scope, use_min_wid, use_lval_wid, rval_type, unsized_flag); - - if (debug_elaborate) { - cerr << expr->get_fileline() << ": debug: r-value tested " - << "type=" << rval_type - << ", width=" << expr_wid - << ", min=" << use_min_wid - << ", unsized_flag=" << (unsized_flag?"true":"false") << endl; - } - - switch (data_type_lv) { + int context_wid = -1; + switch (lv_type) { case IVL_VT_REAL: case IVL_VT_STRING: - unsized_flag = true; - expr_wid = -2; - expr_wid_lv = -1; break; case IVL_VT_BOOL: case IVL_VT_LOGIC: + context_wid = lv_width; break; case IVL_VT_VOID: case IVL_VT_NO_TYPE: ivl_assert(*expr, 0); - expr_wid = -2; - expr_wid_lv = -1; break; } - NetExpr*result = elab_and_eval(des, scope, expr, expr_wid, expr_wid_lv); - return result; + return elab_and_eval(des, scope, expr, context_wid); } /* - * The default behavior for the test_width method is to just return the - * minimum width that is passed in. + * If the mode is UNSIZED, make sure the final expression width is at + * least integer_width, but return the calculated lossless width to + * the caller. */ -unsigned PExpr::test_width(Design*, NetScope*, - unsigned min, unsigned, - ivl_variable_type_t&, bool&) +unsigned PExpr::fix_width_(width_mode_t mode) { - if (debug_elaborate) { - cerr << get_fileline() << ": debug: test_width defaults to " - << min << ", ignoring unsized_flag. typeid=" - << typeid(*this).name() << endl; - } - return min; + unsigned width = expr_width_; + if ((mode == UNSIZED) && (width < integer_width)) + expr_width_ = integer_width; + + return width; } -NetExpr* PExpr::elaborate_expr(Design*des, NetScope*, int, bool) const +unsigned PExpr::test_width(Design*des, NetScope*, width_mode_t&) { - cerr << get_fileline() << ": internal error: I do not know how to elaborate" - << " expression. " << endl; + cerr << get_fileline() << ": internal error: I do not know how to" + << " test the width of this expression. " << endl; + cerr << get_fileline() << ": : Expression is: " << *this + << endl; + des->errors += 1; + return 1; +} + +NetExpr* PExpr::elaborate_expr(Design*des, NetScope*, unsigned, bool) const +{ + cerr << get_fileline() << ": internal error: I do not know how to" + << " elaborate this expression. " << endl; cerr << get_fileline() << ": : Expression is: " << *this << endl; des->errors += 1; return 0; } -unsigned PEBinary::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) + +unsigned PEBinary::test_width(Design*des, NetScope*scope, width_mode_t&mode) { - ivl_variable_type_t expr_type_left = IVL_VT_NO_TYPE; - ivl_variable_type_t expr_type_right= IVL_VT_NO_TYPE; + ivl_assert(*this, left_); + ivl_assert(*this, right_); - bool flag = unsized_flag; + unsigned l_width = left_->test_width(des, scope, mode); - bool flag_left = flag; - unsigned wid_left = left_->test_width(des,scope, min, 0, expr_type_left, flag_left); + width_mode_t saved_mode = mode; - bool flag_right = flag; - unsigned wid_right = right_->test_width(des,scope, min, 0, expr_type_right, flag_right); + unsigned r_width = right_->test_width(des, scope, mode); - if (flag_right && !flag) { - unsized_flag = true; - wid_left = left_->test_width(des, scope, min, 0, expr_type_left, unsized_flag); - } + // If the width mode changed, retest the left operand, as it + // may choose a different width if it is in an unsized context. + if ((mode >= LOSSLESS) && (saved_mode < LOSSLESS)) + l_width = left_->test_width(des, scope, mode); - if (flag_left && !flag) { - unsized_flag = true; - wid_right = right_->test_width(des, scope, min, 0, expr_type_right, unsized_flag); - } + ivl_variable_type_t l_type = left_->expr_type(); + ivl_variable_type_t r_type = right_->expr_type(); - if (expr_type_left == IVL_VT_REAL || expr_type_right == IVL_VT_REAL) + if (l_type == IVL_VT_REAL || r_type == IVL_VT_REAL) expr_type_ = IVL_VT_REAL; - else if (expr_type_left==IVL_VT_LOGIC || expr_type_right==IVL_VT_LOGIC) + else if (l_type == IVL_VT_LOGIC || r_type == IVL_VT_LOGIC) expr_type_ = IVL_VT_LOGIC; else expr_type_ = IVL_VT_BOOL; - switch (op_) { - case '+': - case '-': - if (wid_left > min) - min = wid_left; - if (wid_right > min) - min = wid_right; - if (unsized_flag && type_is_vectorable(expr_type_)) - min += 1; - break; + if (expr_type_ == IVL_VT_REAL) { + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = true; + } else { + expr_width_ = max(l_width, r_width); + min_width_ = max(left_->min_width(), right_->min_width()); + signed_flag_ = left_->has_sign() && right_->has_sign(); - case '*': - if (unsized_flag && type_is_vectorable(expr_type_)) { - unsigned use_wid = wid_left + wid_right; - if (use_wid > integer_width) - use_wid = integer_width; - if (use_wid > min) - min = use_wid; - } - if (wid_left > min) - min = wid_left; - if (wid_right > min) - min = wid_right; - break; + // If the operands are different types, the expression is + // forced to unsigned. In this case the lossless width + // calculation is unreliable and we need to make sure the + // final expression width is at least integer_width. + if ((mode == LOSSLESS) && (left_->has_sign() != right_->has_sign())) + mode = UNSIZED; - case 'l': // << Should be handled by PEBShift - case '<': // < Should be handled by PEBComp - case '>': // > Should be handled by PEBComp - case 'e': // == Should be handled by PEBComp - case 'E': // === Should be handled by PEBComp - case 'L': // <= Should be handled by PEBComp - case 'G': // >= Should be handled by PEBComp - case 'n': // != Should be handled by PEBComp - case 'N': // !== Should be handled by PEBComp - case 'p': // ** should be handled by PEBPower - ivl_assert(*this, 0); - default: - if (wid_left > min) - min = wid_left; - if (wid_right > min) - min = wid_right; - break; + switch (op_) { + case '+': + case '-': + if (mode != SIZED) + expr_width_ += 1; + break; + + case '*': + if (mode != SIZED) + expr_width_ = l_width + r_width; + break; + + case '%': + case '/': + min_width_ = max(min_width_, expr_width_); + break; + + case 'l': // << Should be handled by PEBShift + case 'r': // << Should be handled by PEBShift + case 'R': // << Should be handled by PEBShift + case '<': // < Should be handled by PEBComp + case '>': // > Should be handled by PEBComp + case 'e': // == Should be handled by PEBComp + case 'E': // === Should be handled by PEBComp + case 'L': // <= Should be handled by PEBComp + case 'G': // >= Should be handled by PEBComp + case 'n': // != Should be handled by PEBComp + case 'N': // !== Should be handled by PEBComp + case 'p': // ** should be handled by PEBPower + ivl_assert(*this, 0); + default: + break; + } } - if (type_is_vectorable(expr_type_)) { - // We don't use (need?) lval so make sure the minimum width - // is greater than or equal to the the L-value width. - assert(min >= lval); - expr_width_ = min; - } else - expr_width_ = 1; - - expr_type__ = expr_type_; - return expr_width_; + return fix_width_(mode); } /* @@ -246,104 +223,66 @@ unsigned PEBinary::test_width(Design*des, NetScope*scope, * types. */ NetExpr* PEBinary::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { - assert(left_); - assert(right_); + ivl_assert(*this, left_); + ivl_assert(*this, right_); - // The context passes in the width that this expression is - // expected to use. But if that width is <0, we are in a - // self-determined context and we must use the width that was - // calculated by a previous call to test_with. - int use_wid = expr_wid; - if (use_wid < 0 && expr_type_ != IVL_VT_REAL) { - ivl_assert(*this, expr_width_ > 0); - use_wid = expr_width_; + // Handle the special case that one of the operands is a real + // value and the other is a vector type. In that case, + // elaborate the vectorable argument as self-determined. + // Propagate the expression type (signed/unsigned) down to + // any context-determined operands. + unsigned l_width = expr_wid; + unsigned r_width = expr_wid; + if (left_->expr_type()==IVL_VT_REAL + && type_is_vectorable(right_->expr_type())) { + r_width = right_->expr_width(); + } else { + right_->cast_signed(signed_flag_); + } + if (right_->expr_type()==IVL_VT_REAL + && type_is_vectorable(left_->expr_type())) { + l_width = left_->expr_width(); + } else { + left_->cast_signed(signed_flag_); } - NetExpr*lp = left_->elaborate_expr(des, scope, use_wid, false); - NetExpr*rp = right_->elaborate_expr(des, scope, use_wid, false); + NetExpr*lp = left_->elaborate_expr(des, scope, l_width, false); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width, false); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; return 0; } - // Handle the special case that one of the operands is a real - // value and the other is a vector type. In that case, - // re-elaborate the vectorable argument as self-determined - // lossless. - if (lp->expr_type()==IVL_VT_REAL - && type_is_vectorable(rp->expr_type()) - && expr_wid != -2) { - delete rp; - rp = right_->elaborate_expr(des, scope, -2, false); - } - - if (rp->expr_type()==IVL_VT_REAL - && type_is_vectorable(lp->expr_type()) - && expr_wid != -2) { - delete lp; - lp = left_->elaborate_expr(des, scope, -2, false); - } - - NetExpr*tmp = elaborate_eval_expr_base_(des, lp, rp, expr_wid); - return tmp; -} - -void suppress_binary_operand_sign_if_needed(NetExpr*lp, NetExpr*rp) -{ - // If an argument is a non-vector type, then this type - // suppression does not apply. - if (! type_is_vectorable(lp->expr_type())) - return; - if (! type_is_vectorable(rp->expr_type())) - return; - - // If either operand is unsigned, then treat the whole - // expression as unsigned. This test needs to be done here - // instead of in *_expr_base_ because it needs to be done - // ahead of any subexpression evaluation (because they need to - // know their signedness to evaluate) and because there are - // exceptions to this rule. - if (! lp->has_sign()) - rp->cast_signed(false); - if (! rp->has_sign()) - lp->cast_signed(false); -} - -NetExpr* PEBinary::elaborate_eval_expr_base_(Design*des, - NetExpr*lp, - NetExpr*rp, - int expr_wid) const -{ - /* If either expression can be evaluated ahead of time, then - do so. This can prove helpful later. */ - eval_expr(lp, expr_wid); - eval_expr(rp, expr_wid); + // If either expression can be evaluated ahead of time, then + // do so. This can prove helpful later. + eval_expr(lp, l_width); + eval_expr(rp, r_width); return elaborate_expr_base_(des, lp, rp, expr_wid); } /* - * This is common elaboration of the operator. It presumes that the + * This is the common elaboration of the operator. It presumes that the * operands are elaborated as necessary, and all I need to do is make * the correct NetEBinary object and connect the parameters. */ NetExpr* PEBinary::elaborate_expr_base_(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { if (debug_elaborate) { cerr << get_fileline() << ": debug: elaborate expression " - << *this << " expr_wid=" << expr_wid << endl; + << *this << " expr_width=" << expr_wid << endl; } NetExpr*tmp; switch (op_) { default: - tmp = new NetEBinary(op_, lp, rp); + tmp = new NetEBinary(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); break; @@ -357,9 +296,12 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, return 0; case 'p': - tmp = new NetEBPow(op_, lp, rp); - tmp->set_line(*this); - break; + cerr << get_fileline() << ": internal error: " + << "Elaboration of " << human_readable_op(op_) + << " Should have been handled in NetEBPower::elaborate." + << endl; + des->errors += 1; + return 0; case '*': tmp = elaborate_expr_base_mult_(des, lp, rp, expr_wid); @@ -391,7 +333,8 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, case '+': case '-': - tmp = elaborate_expr_base_add_(des, lp, rp, expr_wid); + tmp = new NetEBAdd(op_, lp, rp, expr_wid, signed_flag_); + tmp->set_line(*this); break; case 'E': /* === */ @@ -411,7 +354,7 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, case 'm': // min(l,r) case 'M': // max(l,r) - tmp = new NetEBMinMax(op_, lp, rp); + tmp = new NetEBMinMax(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); break; } @@ -421,7 +364,7 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, NetExpr* PEBinary::elaborate_expr_base_bits_(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { if (lp->expr_type() == IVL_VT_REAL || rp->expr_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: " @@ -431,20 +374,7 @@ NetExpr* PEBinary::elaborate_expr_base_bits_(Design*des, return 0; } - // If either of the arguments is unsigned, then process both - // of them as unsigned. This only impacts the padding that is - // done to get the operands to the expr_wid. - if (! lp->has_sign()) rp->cast_signed(false); - if (! rp->has_sign()) lp->cast_signed(false); - - if (expr_wid > 0) { - if (type_is_vectorable(lp->expr_type())) - lp = pad_to_width(lp, expr_wid, *this); - if (type_is_vectorable(rp->expr_type())) - rp = pad_to_width(rp, expr_wid, *this); - } - - NetEBBits*tmp = new NetEBBits(op_, lp, rp); + NetEBBits*tmp = new NetEBBits(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; @@ -452,7 +382,7 @@ NetExpr* PEBinary::elaborate_expr_base_bits_(Design*des, NetExpr* PEBinary::elaborate_expr_base_div_(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { /* The % operator does not support real arguments in baseline Verilog. But we allow it in our extended @@ -466,23 +396,7 @@ NetExpr* PEBinary::elaborate_expr_base_div_(Design*des, } } - // If either of the arguments is unsigned, then process both - // of them as unsigned. This only impacts the padding that is - // done to get the operands to the expr_wid. - if (! lp->has_sign()) rp->cast_signed(false); - if (! rp->has_sign()) lp->cast_signed(false); - - /* The original elaboration of the left and right expressions - already tried to elaborate to the expr_wid. If the - expressions are not that width by now, then they need to be - padded. The divide expression operands must be the width - of the output. */ - if (expr_wid > 0) { - lp = pad_to_width(lp, expr_wid, *this); - rp = pad_to_width(rp, expr_wid, *this); - } - - NetEBDiv*tmp = new NetEBDiv(op_, lp, rp); + NetEBDiv*tmp = new NetEBDiv(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; @@ -490,7 +404,7 @@ NetExpr* PEBinary::elaborate_expr_base_div_(Design*des, NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { if (lp->expr_type() == IVL_VT_REAL || rp->expr_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: " @@ -502,20 +416,6 @@ NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des, NetExpr*tmp; - long use_wid = lp->expr_width(); - if (expr_wid > 0) - use_wid = expr_wid; - - if (use_wid == 0) { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Oops, left expression width is not known, " - << "so expression width is not known. Punt." << endl; - tmp = new NetEBShift(op_, lp, rp); - tmp->set_line(*this); - return tmp; - } - // If the left expression is constant, then there are some // special cases we can work with. If the left expression is // not constant, but the right expression is constant, then @@ -524,8 +424,8 @@ NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des, if (NetEConst*lpc = dynamic_cast (lp)) { - // Special case: The left expression is zero. No matter - // what the shift, the result is going to be zero. + // Special case: The left expression is zero. No matter what + // the shift, the result is going to be zero. if (lpc->value().is_defined() && lpc->value().is_zero()) { if (debug_elaborate) @@ -533,92 +433,62 @@ NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des, << "Shift of zero always returns zero." << " Elaborate as constant zero." << endl; - tmp = make_const_0(use_wid); - tmp->set_line(*this); - return tmp; - } + tmp = make_const_0(expr_wid); + tmp->cast_signed(signed_flag_); + tmp->set_line(*this); + return tmp; + } + + // Special case: Both operands are constants. Precalculate + // the entire value here. if (NetEConst*rpc = dynamic_cast (rp)) { - // Handle the super-special case that both - // operands are constants. Precalculate the - // entire value here. - verinum lpval = lpc->value(); unsigned shift = rpc->value().as_ulong(); - verinum result = lpc->value() << shift; - // If the l-value has explicit size, or - // there is a context determined size, use that. - if (lpval.has_len() || expr_wid > 0) { - int use_len = lpval.len(); - if (expr_wid > 0 && expr_wid > use_len) - use_len = expr_wid; - result = verinum(result, use_len); - } - + verinum result (lpc->value() << shift, expr_wid); tmp = new NetEConst(result); + tmp->cast_signed(signed_flag_); + tmp->set_line(*this); + if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Precalculate " << *lpc << " << " << shift << " to constant " << *tmp << " (expr_wid=" << expr_wid << ")" << endl; - } else { - // Handle the special case that the left - // operand is constant. If it is unsized, we - // may have to expand it to an integer width. - verinum lpval = lpc->value(); - if (lpval.len() < integer_width && !lpval.has_len()) { - lpval = verinum(lpval, integer_width); - lpc = new NetEConst(lpval); - lpc->set_line(*lp); - } - - tmp = new NetEBShift(op_, lpc, rp); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Adjust " << *this - << " to this " << *tmp - << " to allow for integer widths." << endl; + return tmp; } } else if (NetEConst*rpc = dynamic_cast (rp)) { long shift = rpc->value().as_long(); - use_wid = lp->expr_width(); - if (expr_wid > 0) - use_wid = expr_wid; - if (shift >= use_wid || (-shift) >= (long)lp->expr_width()) { + // Special case: The shift is at least the size of the entire + // left operand. Elaborate as a constant-0. + if (shift >= expr_wid) { + if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Value left-shifted " << shift - << " beyond width of " << use_wid + << " beyond width of " << expr_wid << ". Elaborate as constant zero." << endl; - tmp = make_const_0(use_wid); + tmp = make_const_0(expr_wid); + tmp->cast_signed(signed_flag_); + tmp->set_line(*this); - } else { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Left shift expression by constant " - << shift << " bits. (use_wid=" << use_wid << ")" << endl; - lp = pad_to_width(lp, use_wid, *this); - tmp = new NetEBShift(op_, lp, rp); + return tmp; } - - } else { - // Left side is not constant, so handle it the - // default way. - if (expr_wid >= 0) - lp = pad_to_width(lp, expr_wid, *this); - tmp = new NetEBShift(op_, lp, rp); } + // Fallback, handle the general case. + tmp = new NetEBShift(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); + return tmp; } NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { if (lp->expr_type() == IVL_VT_REAL || rp->expr_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: " @@ -630,20 +500,6 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des, NetExpr*tmp; - long use_wid = lp->expr_width(); - if (expr_wid > 0) - use_wid = expr_wid; - - if (use_wid == 0) { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Oops, left expression width is not known, " - << "so expression width is not known. Punt." << endl; - tmp = new NetEBShift(op_, lp, rp); - tmp->set_line(*this); - return tmp; - } - if (NetEConst*lpc = dynamic_cast (lp)) { // Special case: The left expression is zero. No matter @@ -655,31 +511,33 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des, << "Shift of zero always returns zero." << " Elaborate as constant zero." << endl; - tmp = make_const_0(use_wid); + tmp = make_const_0(expr_wid); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); + return tmp; } } if (NetEConst*rpc = dynamic_cast (rp)) { - unsigned long shift = rpc->value().as_ulong(); + long shift = rpc->value().as_ulong(); // Special case: The shift is the size of the entire // left operand, and the shift is unsigned. Elaborate as // a constant-0. - if ((op_=='r' || (lp->has_sign()==false)) && - shift >= lp->expr_width()) { + if ((op_=='r' || !signed_flag_) && shift >= expr_wid) { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Value right-shifted " << shift - << " beyond width of " << lp->expr_width() + << " beyond width of " << expr_wid << ". Elaborate as constant zero." << endl; - tmp = make_const_0(use_wid); + tmp = make_const_0(expr_wid); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); - return tmp; + return tmp; } // Special case: the shift is the size of the entire @@ -689,73 +547,51 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des, // The above test assures us that op_ == 'R' && the left // argument is signed when the shift is greater than the // expression width. - if (shift >= lp->expr_width()) { + if (shift >= expr_wid) { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Value signed-right-shifted " << shift - << " beyond width of " << lp->expr_width() + << " beyond width of " << expr_wid << ". Elaborate as replicated top bit." << endl; - ivl_assert(*this, lp->expr_width() > 0); - ivl_assert(*this, use_wid > 0); - - tmp = new NetEConst(verinum(lp->expr_width()-1)); + tmp = new NetEConst(verinum(expr_wid-1)); tmp->set_line(*this); tmp = new NetESelect(lp, tmp, 1); + tmp->set_line(*this); + tmp = pad_to_width(tmp, expr_wid, *this); tmp->cast_signed(true); - tmp = pad_to_width(tmp, use_wid, *this); - return tmp; - } - // If this is lossless, then pad the left expression - // enough to cover the right shift. - if (expr_wid == -2 && use_wid+shift > lp->expr_width()) { - lp->cast_signed(lp->has_sign() && op_=='R'); - lp = pad_to_width(lp, use_wid + shift, *this); + return tmp; } tmp = new NetEConst(verinum(shift)); tmp->set_line(*this); - long tmp_wid = lp->expr_width() - shift; - if (tmp_wid > use_wid) - tmp_wid = use_wid; - ivl_assert(*this, tmp_wid > 0); - ivl_assert(*this, use_wid > 0); + // Pad the left expression enough to cover the right shift. + lp->cast_signed(signed_flag_ && op_=='R'); + lp = pad_to_width(lp, expr_wid + shift, *this); // Implement the right-shift by part-selecting the low - // bits out. Pad the result of the part select back out - // to the desired size. - tmp = new NetESelect(lp, tmp, tmp_wid); + // bits out. + tmp = new NetESelect(lp, tmp, expr_wid); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); - tmp->cast_signed(lp->has_sign() && op_=='R'); - tmp = pad_to_width(tmp, use_wid, *this); + return tmp; } // Fallback, handle the general case. - if (expr_wid > 0) - lp = pad_to_width(lp, expr_wid, *this); - tmp = new NetEBShift(op_, lp, rp); + tmp = new NetEBShift(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); + return tmp; } NetExpr* PEBinary::elaborate_expr_base_mult_(Design*, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { - // First, Make sure that signed arguments are padded to the - // width of the output. This is necessary for 2s complement - // multiplication to come out right. - if (expr_wid > 0) { - if (lp->has_sign() && lp->expr_type() != IVL_VT_REAL) - lp = pad_to_width(lp, expr_wid, *this); - if (rp->has_sign() && rp->expr_type() != IVL_VT_REAL) - rp = pad_to_width(rp, expr_wid, *this); - } - // Keep constants on the right side. if (dynamic_cast(lp)) { NetExpr*tmp = lp; @@ -767,100 +603,65 @@ NetExpr* PEBinary::elaborate_expr_base_mult_(Design*, if (NetEConst*rp_const = dynamic_cast (rp)) { verinum rp_val = rp_const->value(); - int use_wid = expr_wid; - if (use_wid < 0) - use_wid = max(rp->expr_width(), lp->expr_width()); - if (! rp_val.is_defined()) { - NetEConst*tmp = make_const_x(use_wid); + NetEConst*tmp = make_const_x(expr_wid); + tmp->cast_signed(signed_flag_); + tmp->set_line(*this); + return tmp; } if (rp_val.is_zero()) { - NetEConst*tmp = make_const_0(use_wid); + NetEConst*tmp = make_const_0(expr_wid); + tmp->cast_signed(signed_flag_); + tmp->set_line(*this); + return tmp; } } - // If this expression is unsigned, then make sure the - // arguments are unsigned so that the padding below doesn't - // cause any sign extension to happen. - suppress_binary_operand_sign_if_needed(lp, rp); - - // Multiply will guess a width that is the sum of the - // widths of the operand. If that sum is too small, then - // pad one of the arguments enough that the sum is the - // desired width. - if (expr_wid > (long)(lp->expr_width() + rp->expr_width())) - lp = pad_to_width(lp, expr_wid - rp->expr_width(), *this); - - NetEBMult*tmp = new NetEBMult(op_, lp, rp); + NetEBMult*tmp = new NetEBMult(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); - if (expr_wid > 0 && type_is_vectorable(tmp->expr_type())) - tmp->set_width(expr_wid, false); - return tmp; } -NetExpr* PEBinary::elaborate_expr_base_add_(Design*, - NetExpr*lp, NetExpr*rp, - int expr_wid) const +unsigned PEBComp::test_width(Design*des, NetScope*scope, width_mode_t&) { - NetExpr*tmp; - bool use_lossless_flag = expr_wid == -2; + ivl_assert(*this, left_); + ivl_assert(*this, right_); - // If this expression is not vectorable, then do NOT pass the - // lossless flag to the NetEBAdd constructor. For non- - // vectorable, lossless is implicit. - if (! type_is_vectorable(lp->expr_type())) - use_lossless_flag = false; - if (! type_is_vectorable(rp->expr_type())) - use_lossless_flag = false; + // The width and type of a comparison are fixed and well known. + expr_type_ = IVL_VT_LOGIC; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = false; - // If the expression is unsigned, then force the operands to - // unsigned so that the set_width below doesn't cause them to - // be sign-extended. - suppress_binary_operand_sign_if_needed(lp, rp); + // The widths of the operands are semi-self-determined. They + // affect each other, but not the result. + width_mode_t mode = SIZED; - tmp = new NetEBAdd(op_, lp, rp, use_lossless_flag); - if (expr_wid > 0 && type_is_vectorable(tmp->expr_type())) - tmp->set_width(expr_wid); + unsigned l_width = left_->test_width(des, scope, mode); - tmp->set_line(*this); - return tmp; -} + width_mode_t saved_mode = mode; -unsigned PEBComp::test_width(Design*des, NetScope*scope, unsigned, unsigned, - ivl_variable_type_t&my_expr_type, - bool&) -{ - // The width and type of a comparison operator is fixed and - // well known. Set them now. - expr_type_ = IVL_VT_LOGIC; - my_expr_type = expr_type_; - expr_width_ = 1; + unsigned r_width = right_->test_width(des, scope, mode); - // The widths of operands are self-determined, but need to be - // figured out. - bool unsized_flag = false; - ivl_variable_type_t left_type = IVL_VT_NO_TYPE; - unsigned left_width = left_->test_width(des, scope, 0, 0, left_type, unsized_flag); - bool left_unsized_flag = unsized_flag; - ivl_variable_type_t right_type = IVL_VT_NO_TYPE; - unsigned right_width = right_->test_width(des, scope, 0, 0, right_type, unsized_flag); + // If the width mode changed, retest the left operand, as it + // may choose a different width if it is in an unsized context. + if ((mode != SIZED) && (saved_mode == SIZED)) + l_width = left_->test_width(des, scope, mode); - if (left_unsized_flag != unsized_flag) { - left_width = left_->test_width(des, scope, 0, 0, left_type, unsized_flag); - } + ivl_variable_type_t l_type = left_->expr_type(); + ivl_variable_type_t r_type = right_->expr_type(); - int try_wid_l = left_width; - if (type_is_vectorable(left_type) && (right_width > left_width)) - try_wid_l = right_width; + l_width_ = l_width; + if (type_is_vectorable(l_type) && (r_width > l_width)) + l_width_ = r_width; - int try_wid_r = right_width; - if (type_is_vectorable(right_type) && (left_width > right_width)) - try_wid_r = left_width; + r_width_ = r_width; + if (type_is_vectorable(r_type) && (l_width > r_width)) + r_width_ = l_width; // If the expression is unsized and smaller then the integer // minimum, then tweak the size up. @@ -869,52 +670,48 @@ unsigned PEBComp::test_width(Design*des, NetScope*scope, unsigned, unsigned, // exactly the right width to behave just like infinite // width. I suspect that adding 1 more is sufficient in all // cases, but I'm not certain. Ideas? - if (type_is_vectorable(left_type) && unsized_flag && try_wid_l<(int)integer_width) - try_wid_l += 1; - if (type_is_vectorable(right_type) && unsized_flag && try_wid_r<(int)integer_width) - try_wid_r += 1; + if (mode != SIZED) { + if (type_is_vectorable(l_type) && (l_width_ < integer_width)) + l_width_ += 1; + if (type_is_vectorable(r_type) && (r_width_ < integer_width)) + r_width_ += 1; + } if (debug_elaborate) { cerr << get_fileline() << ": debug: " << "Comparison expression operands are " - << left_width << " bits and " - << right_width << " bits. Resorting to " - << try_wid_l << " bits and " - << try_wid_r << " bits." << endl; + << l_type << " " << l_width << " bits and " + << r_type << " " << r_width << " bits. Resorting to " + << l_width_ << " bits and " + << r_width_ << " bits." << endl; } - left_width_ = try_wid_l; - right_width_ = try_wid_r; - - return 1; + return expr_width_; } NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, - int, bool) const + unsigned expr_wid, bool) const { - assert(left_); - assert(right_); + ivl_assert(*this, left_); + ivl_assert(*this, right_); - NetExpr*lp = left_->elaborate_expr(des, scope, left_width_, false); - NetExpr*rp = right_->elaborate_expr(des, scope, right_width_, false); + // Propagate the comparison type (signed/unsigned) down to + // the operands. + if (type_is_vectorable(left_->expr_type()) && !left_->has_sign()) + right_->cast_signed(false); + if (type_is_vectorable(right_->expr_type()) && !right_->has_sign()) + left_->cast_signed(false); + + NetExpr*lp = left_->elaborate_expr(des, scope, l_width_, false); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width_, false); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; return 0; } - suppress_binary_operand_sign_if_needed(lp, rp); - - // The arguments of a compare need to have matching widths, so - // pad the width here. This matters because if the arguments - // are signed, then this padding will do sign extension. - if (type_is_vectorable(lp->expr_type())) - lp = pad_to_width(lp, left_width_, *this); - if (type_is_vectorable(rp->expr_type())) - rp = pad_to_width(rp, right_width_, *this); - - eval_expr(lp, left_width_); - eval_expr(rp, right_width_); + eval_expr(lp, l_width_); + eval_expr(rp, r_width_); // Handle some operand-specific special cases... switch (op_) { @@ -933,49 +730,36 @@ NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, break; } - NetEBComp*tmp = new NetEBComp(op_, lp, rp); + NetExpr*tmp = new NetEBComp(op_, lp, rp); tmp->set_line(*this); - bool flag = tmp->set_width(1); - if (flag == false) { - cerr << get_fileline() << ": internal error: " - "expression bit width of comparison != 1." << endl; - des->errors += 1; - } + + tmp = pad_to_width(tmp, expr_wid, *this); + tmp->cast_signed(signed_flag_); return tmp; } -unsigned PEBLogic::test_width(Design*, NetScope*, - unsigned, unsigned, - ivl_variable_type_t&expr_type_out, - bool&) +unsigned PEBLogic::test_width(Design*, NetScope*, width_mode_t&) { - expr_type_ = IVL_VT_LOGIC; - expr_width_ = 1; - expr_type_out = expr_type_; + // The width and type of a logical operation are fixed. + expr_type_ = IVL_VT_LOGIC; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = false; + + // The widths of the operands are self determined. We don't need + // them now, so they can be tested when they are elaborated. + return expr_width_; } NetExpr*PEBLogic::elaborate_expr(Design*des, NetScope*scope, - int, bool) const + unsigned expr_wid, bool) const { - assert(left_); - assert(right_); + ivl_assert(*this, left_); + ivl_assert(*this, right_); - // The left and right expressions are self-determined and - // independent. Run their test_width methods independently. We - // don't need the widths here, but we do need the expressions - // to calculate their self-determined width and type. - - bool left_flag = false; - ivl_variable_type_t left_type = IVL_VT_NO_TYPE; - left_->test_width(des, scope, 0, 0, left_type, left_flag); - - bool right_flag = false; - ivl_variable_type_t right_type = IVL_VT_NO_TYPE; - right_->test_width(des, scope, 0, 0, right_type, right_flag); - - NetExpr*lp = elab_and_eval(des, scope, left_, -1); + NetExpr*lp = elab_and_eval(des, scope, left_, -1); NetExpr*rp = elab_and_eval(des, scope, right_, -1); if ((lp == 0) || (rp == 0)) { delete lp; @@ -986,103 +770,163 @@ NetExpr*PEBLogic::elaborate_expr(Design*des, NetScope*scope, lp = condition_reduce(lp); rp = condition_reduce(rp); - NetEBLogic*tmp = new NetEBLogic(op_, lp, rp); + NetExpr*tmp = new NetEBLogic(op_, lp, rp); tmp->set_line(*this); - tmp->set_width(1); + + tmp = pad_to_width(tmp, expr_wid, *this); + tmp->cast_signed(signed_flag_); return tmp; } -unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode) { - unsigned wid_left = left_->test_width(des,scope,min, lval, expr_type__, unsized_flag); + ivl_assert(*this, left_); + ivl_assert(*this, right_); - // The right expression is self-determined and has no impact - // on the expression size that is generated. + // The right operand is self determined. Test its type and + // width for use later. We only need to know its width now + // if the left operand is unsized and we need to calculate + // the lossless width. + width_mode_t r_mode = SIZED; + unsigned r_width = right_->test_width(des, scope, r_mode); - if (wid_left < min) - wid_left = min; - if (wid_left < lval) - wid_left = lval; + expr_width_ = left_->test_width(des, scope, mode); + expr_type_ = left_->expr_type(); + signed_flag_ = left_->has_sign(); - if (unsized_flag - && type_is_vectorable(expr_type__) - && wid_left > 0 - && wid_left < integer_width) { - wid_left = integer_width; + if ((mode != SIZED) && type_is_vectorable(expr_type_)) { + // We need to make our best guess at the right operand + // value, to minimise the calculated width. This is + // particularly important for the power operator... - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Test width of unsized " << human_readable_op(op_) - << " is padded to compiler integer width=" << wid_left - << endl; + // Start off by assuming the maximum value for the + // type and width of the right operand. + long r_val = LONG_MAX; + if (r_width < sizeof(long)*8) { + r_val = (1L << r_width) - 1L; + if (right_->has_sign()) + r_val >>= 1; + } + + // If the right operand is constant, we can use the + // actual value. + NetExpr*rp = right_->elaborate_expr(des, scope, r_width, false); + if (rp) { + eval_expr(rp, r_width); + } else { + // error recovery + PEVoid*tmp = new PEVoid(); + tmp->set_line(*this); + delete right_; + right_ = tmp; + } + NetEConst*rc = dynamic_cast (rp); + if (rc && (r_width < sizeof(long)*8)) + r_val = rc->value().as_long(); + + // Clip to a sensible range to avoid underflow/overflow + // in the following calculations. 1024 bits should be + // enough for anyone... + if (r_val < 0) + r_val = 0; + if (r_val > 1024) + r_val = 1024; + + // If the left operand is a simple unsized number, we + // can calculate the actual width required for the power + // operator. + PENumber*lc = dynamic_cast (left_); + + // Now calculate the lossless width. + unsigned use_width = expr_width_; + switch (op_) { + case 'l': // << + use_width += (unsigned)r_val; + break; + + case 'r': // >> + case 'R': // >>> + // A logical shift will effectively coerce a signed + // operand to unsigned. We have to assume an arithmetic + // shift may do the same, as we don't yet know the final + // expression type. + if ((mode == LOSSLESS) && signed_flag_) + mode = UNSIZED; + break; + + case 'p': // ** + if (lc && rc) { + verinum result = pow(lc->value(), rc->value()); + use_width = result.len(); + } else { + if (signed_flag_) use_width -= 1; + use_width *= (unsigned)r_val; + if (signed_flag_) use_width += 2; + } + break; + + default: + cerr << get_fileline() << ": internal error: " + << "Unexpected opcode " << human_readable_op(op_) + << " in PEBLeftWidth::test_width." << endl; + des->errors += 1; + } + + // If the right operand is not constant, we could end up + // grossly overestimating the required width. So in this + // case, don't expand beyond the width of an integer + // (which meets the requirements of the standard). + if ((rc == 0) && (use_width > expr_width_) && (use_width > integer_width)) + use_width = integer_width; + + expr_width_ = use_width; } - expr_type_ = expr_type__; - expr_width_ = wid_left; + if (op_ == 'l') + min_width_ = left_->min_width(); + else + min_width_ = expr_width_; - // Run a test-width on the shift amount so that its types are - // worked out for elaboration later on. We don't need the - // value now. - ivl_variable_type_t rtype = IVL_VT_NO_TYPE; - bool rflag = false; - unsigned wid_right = right_->test_width(des, scope, 0, 0, rtype, rflag); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Test width of exponent of " << op_ << " expression " - << "returns wid=" << wid_right << ", type=" << rtype - << ", flag=" << rflag << endl; - - return expr_width_; + return fix_width_(mode); } NetExpr*PEBLeftWidth::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { - assert(left_); - assert(right_); + ivl_assert(*this, left_); - NetExpr*lp = left_->elaborate_expr(des, scope, expr_wid, false); - if (expr_wid > 0 && lp->expr_width() < (unsigned)expr_wid) { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Pad left operand of " << human_readable_op(op_) - << " to " << expr_wid << "." << endl; - lp = pad_to_width(lp, expr_wid, *this); - } + unsigned r_width = right_->expr_width(); - NetExpr*rp = right_->elaborate_expr(des, scope, -1, false); - if ((lp == 0) || (rp == 0)) { + NetExpr*lp = left_->elaborate_expr(des, scope, expr_wid, false); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width, false); + if (lp == 0 || rp == 0) { delete lp; delete rp; return 0; } - - eval_expr(lp); - eval_expr(rp); + eval_expr(lp, expr_wid); + eval_expr(rp, r_width); return elaborate_expr_leaf(des, lp, rp, expr_wid); } NetExpr*PEBPower::elaborate_expr_leaf(Design*, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { if (debug_elaborate) { cerr << get_fileline() << ": debug: elaborate expression " << *this << " expr_wid=" << expr_wid << endl; } - NetExpr*tmp = new NetEBPow(op_, lp, rp); + NetExpr*tmp = new NetEBPow(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; } NetExpr*PEBShift::elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { NetExpr*tmp = 0; @@ -1107,85 +951,91 @@ NetExpr*PEBShift::elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, } unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) + width_mode_t&mode) { perm_string name = peek_tail_name(path_); - if (name=="$signed"|| name=="$unsigned") { + if (name=="$signed" || name=="$unsigned") { PExpr*expr = parms_[0]; if (expr == 0) return 0; - // The argument width is self-determined. - expr_width_ = expr->test_width(des, scope, 0, 0, expr_type__, unsized_flag); - expr_type_ = expr_type__; + // The argument type/width is self-determined, but affects + // the result width. + width_mode_t arg_mode = SIZED; + expr_width_ = expr->test_width(des, scope, arg_mode); + expr_type_ = expr->expr_type(); + min_width_ = expr->min_width(); + signed_flag_ = (name[1] == 's'); - // The result width is context dependent. - if (expr_width_ > min) - min = expr_width_; + if ((arg_mode != SIZED) && type_is_vectorable(expr_type_)) { + if (mode < LOSSLESS) + mode = LOSSLESS; + if (expr_width_ < integer_width) + expr_width_ = integer_width; + } if (debug_elaborate) - cerr << get_fileline() << ": debug: $signed/$unsigned" - << " argument width = " << expr_width_ - << ", result width = " << min << "." << endl; + cerr << get_fileline() << ": debug: " << name + << " argument width = " << expr_width_ << "." << endl; - // We don't use (need?) lval so make sure the minimum width - // is greater than or equal to the the L-value width. - assert(min >= lval); - return min; - } - - // Run through the arguments of the system function and make - // sure their widths/types are calculated. They are all self- - // determined. - for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { - PExpr*expr = parms_[idx]; - ivl_variable_type_t sub_type = IVL_VT_NO_TYPE; - bool flag = false; - unsigned wid = expr->test_width(des,scope,0,0,sub_type,flag); - if (debug_elaborate) - cerr << get_fileline() << ": debug: test_width" - << " of " << name << " argument " << idx+1 - << " returns type=" << sub_type - << ", wid=" << wid << endl; + return expr_width_; } if (name=="$sizeof" || name=="$bits") { + PExpr*expr = parms_[0]; + if (expr == 0) + return 0; + + // The argument type/width is self-determined and doesn't + // affect the result type/width. + width_mode_t arg_mode = SIZED; + expr->test_width(des, scope, arg_mode); + + expr_type_ = IVL_VT_BOOL; + expr_width_ = integer_width; + min_width_ = integer_width; + signed_flag_ = false; + if (debug_elaborate) cerr << get_fileline() << ": debug: test_width" - << " of $sizeof/$bits returns test_width" + << " of " << name << " returns test_width" << " of compiler integer." << endl; - expr_type_ = IVL_VT_BOOL; - expr_width_= integer_width; - - expr_type__ = expr_type_; return expr_width_; } if (name=="$is_signed") { + PExpr*expr = parms_[0]; + if (expr == 0) + return 0; + + // The argument type/width is self-determined and doesn't + // affect the result type/width. + width_mode_t arg_mode = SIZED; + expr->test_width(des, scope, arg_mode); + + expr_type_ = IVL_VT_BOOL; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = false; + if (debug_elaborate) cerr << get_fileline() << ": debug: test_width" << " of $is_signed returns test_width" << " of 1." << endl; - expr_type_ = IVL_VT_BOOL; - expr_width_ = 1; - expr_type__ = expr_type_; return expr_width_; } /* Get the return type of the system function by looking it up in the sfunc_table. */ - const struct sfunc_return_type*sfunc_info - = lookup_sys_func(peek_tail_name(path_)); + const struct sfunc_return_type*sfunc_info = lookup_sys_func(name); - expr_type_ = sfunc_info->type; - expr_width_ = sfunc_info->wid; - - expr_type__ = expr_type_; + expr_type_ = sfunc_info->type; + expr_width_ = sfunc_info->wid; + min_width_ = expr_width_; + signed_flag_ = sfunc_info->signed_flag; if (debug_elaborate) cerr << get_fileline() << ": debug: test_width " @@ -1197,12 +1047,10 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, } unsigned PECallFunction::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) + width_mode_t&mode) { if (peek_tail_name(path_)[0] == '$') - return test_width_sfunc_(des, scope, min, lval, expr_type__, unsized_flag); + return test_width_sfunc_(des, scope, mode); // The width of user defined functions depends only on the // width of the return value. The arguments are entirely @@ -1212,9 +1060,11 @@ unsigned PECallFunction::test_width(Design*des, NetScope*scope, // If this is an access function, then the width and // type are known by definition. if (find_access_function(path_)) { - expr_type_ = IVL_VT_REAL; - expr_width_ = 1; - expr_type__ = expr_type_; + expr_type_ = IVL_VT_REAL; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = true; + return expr_width_; } @@ -1229,18 +1079,17 @@ unsigned PECallFunction::test_width(Design*des, NetScope*scope, assert(dscope); if (NetNet*res = dscope->find_signal(dscope->basename())) { - expr_type_ = res->data_type(); + expr_type_ = res->data_type(); + expr_width_ = res->vector_width(); + min_width_ = expr_width_; + signed_flag_ = res->get_signed(); + if (debug_elaborate) cerr << get_fileline() << ": debug: test_width " - << "of function returns width " << res->vector_width() + << "of function returns width " << expr_width_ << ", type=" << expr_type_ << "." << endl; - if (! type_is_vectorable(expr_type__)) - unsized_flag = true; - - expr_width_ = res->vector_width(); - expr_type__ = expr_type_; return expr_width_; } @@ -1248,44 +1097,28 @@ unsigned PECallFunction::test_width(Design*des, NetScope*scope, return 0; } -NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, int wid, bool signed_flag) const +NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, unsigned wid) const { /* If the expression is a const, then replace it with a new const. This is a more efficient result. */ if (NetEConst*tmp = dynamic_cast(expr)) { - /* If this is an unsized signed constant then we may need - * to extend it up to integer_width. */ - if (! tmp->has_width() && tmp->has_sign() && (wid > 0)) { - unsigned pwidth = tmp->expr_width(); - if ((unsigned)wid > integer_width) { - if (integer_width > pwidth) pwidth = integer_width; - } else { - if ((unsigned)wid > pwidth) pwidth = wid; - } - tmp = dynamic_cast(pad_to_width(tmp, pwidth, - *expr)); - assert(tmp); - } - tmp->cast_signed(signed_flag); - if (wid > (int)(tmp->expr_width())) { - verinum oval = pad_to_width(tmp->value(), wid); - tmp = new NetEConst(oval); + tmp->cast_signed(signed_flag_); + if (wid > tmp->expr_width()) { + tmp = new NetEConst(verinum(tmp->value(), wid)); tmp->set_line(*this); delete expr; } return tmp; } - if (wid < 0) - wid = expr->expr_width(); - if (debug_elaborate) cerr << get_fileline() << ": debug: cast to " << wid - << " bits" << endl; + << " bits " << (signed_flag_?"signed":"unsigned") << endl; NetESelect*tmp = new NetESelect(expr, 0, wid); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); - tmp->cast_signed(signed_flag); + return tmp; } @@ -1295,16 +1128,18 @@ NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, int wid, bool signed_flag) * size_tf functions, make assumptions about widths based on some * known function names. */ -NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_wid) const +NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, + unsigned expr_wid) const { + perm_string name = peek_tail_name(path_); /* Catch the special case that the system function is the $signed function. Its argument will be evaluated as a self-determined expression. */ - if (strcmp(peek_tail_name(path_), "$signed") == 0) { + if (name=="$signed" || name=="$unsigned") { if ((parms_.size() != 1) || (parms_[0] == 0)) { - cerr << get_fileline() << ": error: The $signed() function " - << "takes exactly one(1) argument." << endl; + cerr << get_fileline() << ": error: The " << name + << " function takes exactly one(1) argument." << endl; des->errors += 1; return 0; } @@ -1312,93 +1147,54 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w PExpr*expr = parms_[0]; NetExpr*sub = expr->elaborate_expr(des, scope, expr_width_, true); - return cast_to_width_(sub, expr_wid, true); - } - /* As above, for the $unsigned function. */ - if (strcmp(peek_tail_name(path_), "$unsigned") == 0) { - if ((parms_.size() != 1) || (parms_[0] == 0)) { - cerr << get_fileline() << ": error: The $unsigned() function " - << "takes exactly one(1) argument." << endl; - des->errors += 1; - return 0; - } - - PExpr*expr = parms_[0]; - NetExpr*sub = expr->elaborate_expr(des, scope, expr_width_, true); - - return cast_to_width_(sub, expr_wid, false); + return cast_to_width_(sub, expr_wid); } /* Interpret the internal $sizeof system function to return the bit width of the sub-expression. The value of the sub-expression is not used, so the expression itself can be deleted. */ - if ((strcmp(peek_tail_name(path_), "$sizeof") == 0) - || (strcmp(peek_tail_name(path_), "$bits") == 0)) { + if (name=="$sizeof" || name=="$bits") { if ((parms_.size() != 1) || (parms_[0] == 0)) { - cerr << get_fileline() << ": error: The $bits() function " - << "takes exactly one(1) argument." << endl; + cerr << get_fileline() << ": error: The " << name + << " function takes exactly one(1) argument." << endl; des->errors += 1; return 0; } - if (strcmp(peek_tail_name(path_), "$sizeof") == 0) + if (name=="$sizeof") cerr << get_fileline() << ": warning: $sizeof is deprecated." << " Use $bits() instead." << endl; PExpr*expr = parms_[0]; - ivl_assert(*this, expr); - /* Elaborate the sub-expression to get its - self-determined width, and save that width. Then - delete the expression because we don't really want - the expression itself. */ - long sub_expr_width = 0; - if (NetExpr*tmp = expr->elaborate_expr(des, scope, -1, true)) { - sub_expr_width = tmp->expr_width(); - delete tmp; - } - - verinum val ( (uint64_t)sub_expr_width, 8*sizeof(unsigned)); + verinum val ( (uint64_t)expr->expr_width(), integer_width); NetEConst*sub = new NetEConst(val); sub->set_line(*this); - return sub; + return cast_to_width_(sub, expr_wid); } /* Interpret the internal $is_signed system function to return a single bit flag -- 1 if the expression is signed, 0 - otherwise. The subexpression is elaborated but not - evaluated. */ - if (strcmp(peek_tail_name(path_), "$is_signed") == 0) { + otherwise. */ + if (name=="$is_signed") { if ((parms_.size() != 1) || (parms_[0] == 0)) { - cerr << get_fileline() << ": error: The $is_signed() function " - << "takes exactly one(1) argument." << endl; + cerr << get_fileline() << ": error: The " << name + << " function takes exactly one(1) argument." << endl; des->errors += 1; return 0; } PExpr*expr = parms_[0]; - NetExpr*sub = expr->elaborate_expr(des, scope, -1, true); - verinum val (sub->has_sign()? verinum::V1 : verinum::V0, 1); - delete sub; - - sub = new NetEConst(val); + verinum val (expr->has_sign()? verinum::V1 : verinum::V0, 1); + NetEConst*sub = new NetEConst(val); sub->set_line(*this); - return sub; + return cast_to_width_(sub, expr_wid); } - /* Get the return type of the system function by looking it up - in the sfunc_table. */ - const struct sfunc_return_type*sfunc_info - = lookup_sys_func(peek_tail_name(path_)); - - ivl_variable_type_t sfunc_type = sfunc_info->type; - unsigned wid = sfunc_info->wid; - - /* How many parameters are there? The Verilog language allows empty parameters in certain contexts, so the parser will allow things like func(1,,3). It will also cause func() to @@ -1411,11 +1207,8 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w if ((nparms == 1) && (parms_[0] == 0)) nparms = 0; - NetESFunc*fun = new NetESFunc(peek_tail_name(path_), sfunc_type, - wid, nparms); + NetESFunc*fun = new NetESFunc(name, expr_type_, expr_width_, nparms); fun->set_line(*this); - if (sfunc_info->signed_flag) - fun->cast_signed(true); /* Now run through the expected parameters. If we find that there are missing parameters, print an error message. @@ -1428,9 +1221,8 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w for (unsigned idx = 0 ; idx < nparms ; idx += 1) { PExpr*expr = parms_[idx]; if (expr) { - NetExpr*tmp1 = expr->elaborate_expr(des, scope, -1, true); - eval_expr(tmp1); - fun->parm(idx, tmp1); + NetExpr*tmp = elab_sys_task_arg(des, scope, name, idx, expr); + fun->parm(idx, tmp); } else { missing_parms += 1; @@ -1439,19 +1231,22 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w } if (missing_parms > 0) { - cerr << get_fileline() << ": error: The function " - << peek_tail_name(path_) + cerr << get_fileline() << ": error: The function " << name << " has been called with empty parameters." << endl; cerr << get_fileline() << ": : Verilog doesn't allow " << "passing empty parameters to functions." << endl; des->errors += 1; } - return fun; + NetExpr*tmp = pad_to_width(fun, expr_wid, *this); + tmp->cast_signed(signed_flag_); + + return tmp; } NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope, - ivl_nature_t nature) const + ivl_nature_t nature, + unsigned expr_wid) const { // An access function must have 1 or 2 arguments. ivl_assert(*this, parms_.size()==2 || parms_.size()==1); @@ -1500,14 +1295,17 @@ NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope, ivl_assert(*this, 0); } - NetEAccess*tmp = new NetEAccess(branch, nature); + NetExpr*tmp = new NetEAccess(branch, nature); tmp->set_line(*this); + tmp = pad_to_width(tmp, expr_wid, *this); + tmp->cast_signed(signed_flag_); + return tmp; } NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { if (peek_tail_name(path_)[0] == '$') return elaborate_sfunc_(des, scope, expr_wid); @@ -1519,7 +1317,8 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, // way. ivl_nature_t access_nature = find_access_function(path_); if (access_nature) - return elaborate_access_func_(des, scope, access_nature); + return elaborate_access_func_(des, scope, access_nature, + expr_wid); // We do not currently support constant user function and // this is where things fail because of that, though we @@ -1546,8 +1345,6 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, if ((parms_count == 1) && (parms_[0] == 0)) parms_count = 0; - - svector parms (parms_count); /* Elaborate the input expressions for the function. This is @@ -1561,7 +1358,7 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, if (tmp) { parms[idx] = elaborate_rval_expr(des, scope, def->port(idx)->data_type(), - def->port(idx)->vector_width(), + (unsigned)def->port(idx)->vector_width(), tmp); if (NetEEvent*evt = dynamic_cast (parms[idx])) { cerr << evt->get_fileline() << ": error: An event '" @@ -1603,8 +1400,11 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, NetESignal*eres = new NetESignal(res); NetEUFunc*func = new NetEUFunc(scope, dscope, eres, parms); func->set_line(*this); - func->cast_signed(res->get_signed()); - return func; + + NetExpr*tmp = pad_to_width(func, expr_wid, *this); + tmp->cast_signed(signed_flag_); + + return tmp; } cerr << get_fileline() << ": internal error: Unable to locate " @@ -1614,76 +1414,23 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, return 0; } -unsigned PEConcat::test_width(Design*des, NetScope*scope, - unsigned, unsigned, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEConcat::test_width(Design*des, NetScope*scope, width_mode_t&) { - expr_type_ = IVL_VT_LOGIC; - - unsigned count_width = 0; + expr_width_ = 0; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { - tested_widths_[idx] = parms_[idx]->test_width(des, scope, 0, 0, expr_type__, unsized_flag); - count_width += tested_widths_[idx]; + expr_width_ += parms_[idx]->test_width(des, scope, width_modes_[idx]); } - if (repeat_) { - unsigned repeat_count = 1; - - // The repeat expression is self-determined and - // its own type. - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool tmp_flag = false; - repeat_->test_width(des, scope, 0, 0, tmp_type, tmp_flag); - - // Try to evaluate the repeat expression now, so - // that we can give the caller an accurate - // expression width. - NetExpr*tmp = elab_and_eval(des, scope, repeat_, -1); - if (NetEConst*tmp_c = dynamic_cast (tmp)) { - repeat_count = tmp_c->value().as_ulong(); - - } else { - // Gack! Can't evaluate expression yet! - // Unfortunately, it is possible that this - // expression may turn out to be constant later in - // elaboration, so we can't really get away with - // reporting an error. - repeat_count = 1; - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "CONCAT MISSING TEST_WIDTH WHEN REPEAT IS PRESENT!" - << endl; - } - count_width *= repeat_count; - } - - expr_type__ = expr_type_; - unsized_flag = false; - return count_width; -} - -// Keep track of the concatenation/repeat depth. -static int concat_depth = 0; - -NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const -{ - concat_depth += 1; - NetExpr* repeat = 0; - - if (debug_elaborate) { - cerr << get_fileline() << ": debug: Elaborate expr=" << *this - << ", expr_wid=" << expr_wid << endl; - } + expr_type_ = IVL_VT_LOGIC; + signed_flag_ = false; /* If there is a repeat expression, then evaluate the constant value and set the repeat count. */ - if (repeat_) { + if (repeat_ && (scope != tested_scope_)) { need_constant_expr = true; NetExpr*tmp = elab_and_eval(des, scope, repeat_, -1); need_constant_expr = false; - assert(tmp); + if (tmp == 0) return 0; if (tmp->expr_type() == IVL_VT_REAL) { cerr << tmp->get_fileline() << ": error: Concatenation " @@ -1696,7 +1443,7 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, if (rep == 0) { cerr << get_fileline() << ": error: " - "Concatenation repeat expression cannot be evaluated." + "Concatenation repeat expression is not constant." << endl; cerr << get_fileline() << ": : The expression is: " << *tmp << endl; @@ -1709,7 +1456,6 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, << "may not be undefined (" << rep->value() << ")." << endl; des->errors += 1; - concat_depth -= 1; return 0; } @@ -1718,23 +1464,43 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, << "may not be negative (" << rep->value().as_long() << ")." << endl; des->errors += 1; - concat_depth -= 1; return 0; } - if (rep->value().is_zero() && concat_depth < 2) { - cerr << get_fileline() << ": error: Concatenation repeat " - << "may not be zero in this context." << endl; - des->errors += 1; - concat_depth -= 1; - return 0; - } + repeat_count_ = rep->value().as_ulong(); - repeat = rep; + tested_scope_ = scope; + } + expr_width_ *= repeat_count_; + min_width_ = expr_width_; + + return expr_width_; +} + +// Keep track of the concatenation/repeat depth. +static int concat_depth = 0; + +NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, + unsigned expr_wid, bool) const +{ + concat_depth += 1; + + if (debug_elaborate) { + cerr << get_fileline() << ": debug: Elaborate expr=" << *this + << ", expr_wid=" << expr_wid << endl; + } + + if (repeat_count_ == 0 && concat_depth < 2) { + cerr << get_fileline() << ": error: Concatenation repeat " + << "may not be zero in this context." << endl; + des->errors += 1; + concat_depth -= 1; + return 0; } unsigned wid_sum = 0; unsigned parm_cnt = 0; + unsigned parm_errors = 0; svector parms(parms_.size()); /* Elaborate all the parameters and attach them to the concat node. */ @@ -1747,25 +1513,29 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, } assert(parms_[idx]); - NetExpr*ex = elab_and_eval(des, scope, parms_[idx], - tested_widths_[idx], 0); + unsigned wid = parms_[idx]->expr_width(); + NetExpr*ex = parms_[idx]->elaborate_expr(des, scope, wid, false); if (ex == 0) continue; ex->set_line(*parms_[idx]); + eval_expr(ex, -1); + if (ex->expr_type() == IVL_VT_REAL) { cerr << ex->get_fileline() << ": error: " << "Concatenation operand can not be real: " << *parms_[idx] << endl; des->errors += 1; + parm_errors += 1; continue; } - if (! ex->has_width()) { + if (width_modes_[idx] != SIZED) { cerr << ex->get_fileline() << ": error: " << "Concatenation operand \"" << *parms_[idx] << "\" has indefinite width." << endl; des->errors += 1; + parm_errors += 1; continue; } @@ -1778,29 +1548,34 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, } wid_sum += ex->expr_width(); } + if (parm_errors) { + concat_depth -= 1; + return 0; + } /* Make the empty concat expression. */ - NetEConcat*tmp = new NetEConcat(parm_cnt, repeat); - tmp->set_line(*this); + NetEConcat*concat = new NetEConcat(parm_cnt, repeat_count_); + concat->set_line(*this); /* Remove any zero width constants. */ unsigned off = 0; for (unsigned idx = 0 ; idx < parm_cnt ; idx += 1) { while (parms[off+idx] == 0) off += 1; - tmp->set(idx, parms[off+idx]); + concat->set(idx, parms[off+idx]); } - tmp->set_width(wid_sum * tmp->repeat()); - if (wid_sum == 0 && concat_depth < 2) { cerr << get_fileline() << ": error: Concatenation may not " << "have zero width in this context." << endl; des->errors += 1; concat_depth -= 1; - delete tmp; + delete concat; return 0; } + NetExpr*tmp = pad_to_width(concat, expr_wid, *this); + tmp->cast_signed(signed_flag_); + concat_depth -= 1; return tmp; } @@ -1812,24 +1587,20 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, * * Absent any better idea, we call all real valued results a width of 1. */ -unsigned PEFNumber::test_width(Design*, NetScope*, - unsigned, unsigned, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEFNumber::test_width(Design*, NetScope*, width_mode_t&) { - expr_type_ = IVL_VT_REAL; - expr_width_ = 1; - unsized_flag = false; + expr_type_ = IVL_VT_REAL; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = true; - expr_type__ = expr_type_; - return 1; + return expr_width_; } -NetExpr* PEFNumber::elaborate_expr(Design*, NetScope*, int, bool) const +NetExpr* PEFNumber::elaborate_expr(Design*, NetScope*, unsigned, bool) const { NetECReal*tmp = new NetECReal(*value_); tmp->set_line(*this); - tmp->set_width(1U, false); return tmp; } @@ -1849,20 +1620,12 @@ bool PEIdent::calculate_parts_(Design*des, NetScope*scope, ivl_assert(*this, index_tail.sel == index_component_t::SEL_PART); ivl_assert(*this, index_tail.msb && index_tail.lsb); - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool tmp_flag = false; - int msb_wid = index_tail.msb->test_width(des, scope, 0, 0, tmp_type, tmp_flag); - - tmp_type = IVL_VT_NO_TYPE; - tmp_flag = false; - int lsb_wid = index_tail.lsb->test_width(des, scope, 0, 0, tmp_type, tmp_flag); - /* This handles part selects. In this case, there are two bit select expressions, and both must be constant. Evaluate them and pass the results back to the caller. */ need_constant_expr = true; - NetExpr*lsb_ex = elab_and_eval(des, scope, index_tail.lsb, lsb_wid); + NetExpr*lsb_ex = elab_and_eval(des, scope, index_tail.lsb, -1); need_constant_expr = false; NetEConst*lsb_c = dynamic_cast(lsb_ex); if (lsb_c == 0) { @@ -1882,7 +1645,7 @@ bool PEIdent::calculate_parts_(Design*des, NetScope*scope, } need_constant_expr = true; - NetExpr*msb_ex = elab_and_eval(des, scope, index_tail.msb, msb_wid); + NetExpr*msb_ex = elab_and_eval(des, scope, index_tail.msb, -1); need_constant_expr = false; NetEConst*msb_c = dynamic_cast(msb_ex); if (msb_c == 0) { @@ -1920,7 +1683,6 @@ bool PEIdent::calculate_up_do_width_(Design*des, NetScope*scope, /* Calculate the width expression (in the lsb_ position) first. If the expression is not constant, error but guess 1 so we can keep going and find more errors. */ - probe_expr_width(des, scope, index_tail.lsb); need_constant_expr = true; NetExpr*wid_ex = elab_and_eval(des, scope, index_tail.lsb, -1); need_constant_expr = false; @@ -1953,7 +1715,6 @@ NetExpr* PEIdent::calculate_up_do_base_(Design*des, NetScope*scope) const ivl_assert(*this, index_tail.lsb != 0); ivl_assert(*this, index_tail.msb != 0); - probe_expr_width(des, scope, index_tail.msb); NetExpr*tmp = elab_and_eval(des, scope, index_tail.msb, -1); return tmp; } @@ -1985,23 +1746,7 @@ bool PEIdent::calculate_param_range_(Design*, NetScope*, return true; } -static void probe_index_expr_width(Design*des, NetScope*scope, - const name_component_t&name) -{ - for (list::const_iterator cur = name.index.begin() - ; cur != name.index.end() ; ++ cur ) { - - if (cur->msb) - probe_expr_width(des, scope, cur->msb); - if (cur->lsb) - probe_expr_width(des, scope, cur->lsb); - } -} - -unsigned PEIdent::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) { NetNet* net = 0; const NetExpr*par = 0; @@ -2018,7 +1763,6 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, 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()) { - probe_index_expr_width(des, scope, name_tail); const index_component_t&index_tail = name_tail.index.back(); // Skip full array word net selects. if (!net || (name_tail.index.size() > net->array_dimensions())) { @@ -2059,17 +1803,21 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, } if (use_width != UINT_MAX) { - expr_type_ = IVL_VT_LOGIC; // Assume bit/parts selects are logic - expr_width_ = max(use_width, min); - expr_type__ = expr_type_; + expr_type_ = IVL_VT_LOGIC; // Assume bit/parts selects are logic + expr_width_ = use_width; + min_width_ = use_width; + signed_flag_ = false; + return expr_width_; } // The width of a signal expression is the width of the signal. if (net != 0) { - expr_type_ = net->data_type(); - expr_width_= max(net->vector_width(), (unsigned long)min); - expr_type__ = expr_type_; + expr_type_ = net->data_type(); + expr_width_ = net->vector_width(); + min_width_ = expr_width_; + signed_flag_ = net->get_signed(); + return expr_width_; } @@ -2079,35 +1827,51 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, netenum_t*use_enum = par_enum->enumeration(); ivl_assert(*this, use_enum != 0); - expr_type_ = use_enum->base_type(); - unsized_flag = false; - expr_width_ = max(use_enum->base_width(), min); - expr_type__ = expr_type_; + expr_type_ = use_enum->base_type(); + expr_width_ = use_enum->base_width(); + min_width_ = expr_width_; + signed_flag_ = false; + return expr_width_; } // The width of a parameter is the width of the parameter value // (as evaluated earlier). if (par != 0) { - expr_type_ = par->expr_type(); - expr_width_ = par->expr_width(); - expr_type__ = expr_type_; - if (!par->has_width()) - unsized_flag = true; + expr_type_ = par->expr_type(); + expr_width_ = par->expr_width(); + min_width_ = expr_width_; + signed_flag_ = par->has_sign(); + + if ((mode < LOSSLESS) && !par->has_width()) + mode = LOSSLESS; + return expr_width_; } + if (path_.size() == 1 + && scope->genvar_tmp.str() + && strcmp(peek_tail_name(path_), scope->genvar_tmp) == 0) { + verinum val (scope->genvar_tmp_val); + expr_type_ = IVL_VT_BOOL; + expr_width_ = val.len(); + min_width_ = expr_width_; + signed_flag_ = true; + + if (mode < LOSSLESS) + mode = LOSSLESS; + + return expr_width_; + } + // Not a net, and not a parameter? Give up on the type, but - // set the width that we collected. - expr_type_ = IVL_VT_NO_TYPE; - expr_width_ = min; + // set the width to 0. + expr_type_ = IVL_VT_NO_TYPE; + expr_width_ = 0; + min_width_ = 0; + signed_flag_ = false; - // We don't use (need?) lval so make sure the minimum width - // is greater than or equal to the the L-value width. - assert(min >= lval); - - expr_type__ = expr_type_; - return min; + return expr_width_; } /* @@ -2122,7 +1886,7 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, * The signal name may be escaped, but that affects nothing here. */ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool sys_task_arg) const + unsigned expr_wid, bool sys_task_arg) const { assert(scope); @@ -2145,9 +1909,18 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, ex1, ex2); // If the identifier name is a parameter name, then return - // a reference to the parameter expression. - if (par != 0) - return elaborate_expr_param_(des, scope, par, found_in, ex1, ex2, expr_wid); + // the parameter value. + if (par != 0) { + NetExpr*tmp = elaborate_expr_param_(des, scope, par, found_in, + ex1, ex2, expr_wid); + + if (!tmp) return 0; + + tmp = pad_to_width(tmp, expr_wid, *this); + tmp->cast_signed(signed_flag_); + + return tmp; + } // If this is a parameter expression, no other identifiers are valid. if (is_param_expr) { @@ -2160,8 +1933,17 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, // If the identifier names a signal (a register or wire) // then create a NetESignal node to handle it. - if (net != 0) - return elaborate_expr_net(des, scope, net, found_in, sys_task_arg); + if (net != 0) { + NetExpr*tmp = elaborate_expr_net(des, scope, net, found_in, + expr_wid, sys_task_arg); + + if (!tmp) return 0; + + tmp = pad_to_width(tmp, expr_wid, *this); + tmp->cast_signed(signed_flag_); + + return tmp; + } // If the identifier is a named event. // is a variable reference. @@ -2181,7 +1963,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, cerr << get_fileline() << ": debug: " << path_ << " is genvar with value " << scope->genvar_tmp_val << "." << endl; - verinum val (scope->genvar_tmp_val); + verinum val (scope->genvar_tmp_val, expr_wid); NetEConst*tmp = new NetEConst(val); tmp->set_line(*this); return tmp; @@ -2259,7 +2041,8 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, return tmp; } - NetExpr*expr = elaborate_expr_net(des, scope, net, found_in, false); + NetExpr*expr = elaborate_expr_net(des, scope, net, found_in, + expr_wid, false); NetESFunc*sys_expr = 0; if (method_name == "name") { @@ -2278,7 +2061,8 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, << "Unknown method name `" << method_name << "'" << " attached to " << use_path << "." << endl; des->errors += 1; - return elaborate_expr_net(des, scope, net, found_in, false); + return elaborate_expr_net(des, scope, net, found_in, + expr_wid, false); } sys_expr->set_line(*this); @@ -2393,7 +2177,8 @@ NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope, const NetExpr*par, NetScope*, const NetExpr*par_msb, - const NetExpr*par_lsb) const + const NetExpr*par_lsb, + unsigned expr_wid) const { long msv, lsv; bool parts_defined_flag; @@ -2422,7 +2207,8 @@ NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope, "Replacing select with a constant 'bx." << endl; } - NetEConst*tmp = new NetEConst(verinum(verinum::Vx, 1, false)); + verinum val(verinum::Vx, expr_wid, true); + NetEConst*tmp = new NetEConst(val); tmp->set_line(*this); return tmp; } @@ -2683,7 +2469,7 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb, - int expr_wid) const + unsigned expr_wid) const { const name_component_t&name_tail = path_.back(); index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; @@ -2705,7 +2491,7 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, // selects to different methods. if (use_sel == index_component_t::SEL_PART) return elaborate_expr_param_part_(des, scope, par, found_in, - par_msb, par_lsb); + par_msb, par_lsb, expr_wid); if (use_sel == index_component_t::SEL_IDX_UP) return elaborate_expr_param_idx_up_(des, scope, par, found_in, @@ -2719,6 +2505,8 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, // rewritten in the above format, as I get to it. NetExpr*tmp = par->dup_expr(); + if (!tmp) + return 0; if (use_sel == index_component_t::SEL_BIT) { ivl_assert(*this, !name_tail.index.empty()); @@ -2736,8 +2524,7 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, /* Handle the case where a parameter has a bit select attached to it. Generate a NetESelect object to select the bit as desired. */ - NetExpr*mtmp = index_tail.msb->elaborate_expr(des, scope, -1,false); - eval_expr(mtmp); + NetExpr*mtmp = elab_and_eval(des, scope, index_tail.msb, -1); /* Let's first try to get constant values for both the parameter and the bit select. If they are @@ -2841,21 +2628,21 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Elaborate parameter <" << path_ - << "> as enumeration constant." << endl; + << "> as enumeration constant." << *etmp << endl; tmp = etmp->dup_expr(); + tmp = pad_to_width(tmp, expr_wid, *this); } else { /* No bit or part select. Make the constant into a - NetEConstParam if possible. */ + NetEConstParam or NetECRealParam as appropriate. */ NetEConst*ctmp = dynamic_cast(tmp); if (ctmp != 0) { + verinum cvalue = cast_to_width(ctmp->value(), expr_wid); + perm_string name = peek_tail_name(path_); NetEConstParam*ptmp - = new NetEConstParam(found_in, name, ctmp->value()); - - if (expr_wid > 0) - ptmp->set_width((unsigned)expr_wid); + = new NetEConstParam(found_in, name, cvalue); if (debug_elaborate) cerr << get_fileline() << ": debug: " @@ -2889,6 +2676,7 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, */ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, NetNet*net, NetScope*found_in, + unsigned expr_wid, bool sys_task_arg) const { const name_component_t&name_tail = path_.back(); @@ -2978,7 +2766,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, } if (word_sel == index_component_t::SEL_PART) - return elaborate_expr_net_part_(des, scope, res, found_in); + return elaborate_expr_net_part_(des, scope, res, found_in, expr_wid); if (word_sel == index_component_t::SEL_IDX_UP) return elaborate_expr_net_idx_up_(des, scope, res, found_in); @@ -2998,7 +2786,8 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, * Handle part selects of NetNet identifiers. */ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, - NetESignal*net, NetScope*) const + NetESignal*net, NetScope*, + unsigned expr_wid) const { long msv, lsv; bool parts_defined_flag; @@ -3028,7 +2817,7 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, "Replacing select with a constant 'bx." << endl; } - NetEConst*tmp = new NetEConst(verinum(verinum::Vx, 1, false)); + NetEConst*tmp = new NetEConst(verinum(verinum::Vx, expr_wid, true)); tmp->set_line(*this); return tmp; } @@ -3103,7 +2892,7 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, * Part select indexed up, i.e. net[ +: ] */ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, - NetESignal*net, NetScope*) const + NetESignal*net, NetScope*) const { NetExpr*base = calculate_up_do_base_(des, scope); @@ -3191,7 +2980,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, * Part select indexed down, i.e. net[ -: ] */ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, - NetESignal*net, NetScope*)const + NetESignal*net, NetScope*)const { NetExpr*base = calculate_up_do_base_(des, scope); @@ -3275,7 +3064,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, } NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, - NetESignal*net, NetScope*) const + NetESignal*net, NetScope*) const { const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); @@ -3376,10 +3165,12 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, NetNet*net, NetScope*found_in, + unsigned expr_wid, bool sys_task_arg) const { if (net->array_dimensions() > 0) - return elaborate_expr_net_word_(des, scope, net, found_in, sys_task_arg); + return elaborate_expr_net_word_(des, scope, net, found_in, + expr_wid, sys_task_arg); NetESignal*node = new NetESignal(net); node->set_line(*this); @@ -3403,7 +3194,8 @@ NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, // selected bits. The lsb_ and msb_ expressions are from // the foo[msb:lsb] expression in the original. if (use_sel == index_component_t::SEL_PART) - return elaborate_expr_net_part_(des, scope, node, found_in); + return elaborate_expr_net_part_(des, scope, node, found_in, + expr_wid); if (use_sel == index_component_t::SEL_IDX_UP) return elaborate_expr_net_idx_up_(des, scope, node, found_in); @@ -3422,104 +3214,72 @@ NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, return node; } -unsigned PENumber::test_width(Design*, NetScope*, - unsigned min, unsigned lval, - ivl_variable_type_t&use_expr_type, - bool&unsized_flag) +unsigned PENumber::test_width(Design*, NetScope*, width_mode_t&mode) { - expr_type_ = IVL_VT_LOGIC; - unsigned use_wid = value_->len(); - if (min > use_wid) - use_wid = min; + expr_type_ = IVL_VT_LOGIC; + expr_width_ = value_->len(); + min_width_ = 1; + signed_flag_ = value_->has_sign(); - if (! value_->has_len()) - unsized_flag = true; + if ((mode < LOSSLESS) && !value_->has_len()) + mode = LOSSLESS; - if (lval > 0 && lval < use_wid) - use_wid = lval; - - use_expr_type = expr_type_; - expr_width_ = use_wid; - return use_wid; + return expr_width_; } NetEConst* PENumber::elaborate_expr(Design*, NetScope*, - int expr_width__, bool) const + unsigned expr_wid, bool) const { assert(value_); - verinum tvalue = *value_; - - // If the expr_width is >0, then the context is requesting a - // specific size (for example this is part of the r-values of - // an assignment) so we pad to the desired width and ignore - // the self-determined size. - if (expr_width__ > 0) { - tvalue = pad_to_width(tvalue, expr_width__); - if ( (tvalue.len() > (unsigned)expr_width__) || - (is_param_expr && !tvalue.has_len()) ) { - verinum tmp (tvalue, expr_width__); - tmp.has_sign(tvalue.has_sign()); - tvalue = tmp; - } - } - - NetEConst*tmp = new NetEConst(tvalue); + verinum val = *value_; + if (val.has_len()) + val.has_sign(signed_flag_); + val = cast_to_width(val, expr_wid); + NetEConst*tmp = new NetEConst(val); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); + return tmp; } -unsigned PEString::test_width(Design*, NetScope*, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&) +unsigned PEString::test_width(Design*, NetScope*, width_mode_t&) { - expr_type_ = IVL_VT_BOOL; - expr_width_ = text_? 8*strlen(text_) : 0; - if (min > expr_width_) - expr_width_ = min; + expr_type_ = IVL_VT_BOOL; + expr_width_ = text_? verinum(text_).len() : 0; + min_width_ = expr_width_; + signed_flag_ = false; - expr_type__ = expr_type_; - // We don't use (need?) lval so make sure the minimum width - // is greater than or equal to the the L-value width. - assert(expr_width_ >= lval); return expr_width_; } NetEConst* PEString::elaborate_expr(Design*, NetScope*, - int, bool) const + unsigned expr_wid, bool) const { - NetEConst*tmp = new NetEConst(value()); + verinum val(value()); + val = pad_to_width(val, expr_wid); + NetEConst*tmp = new NetEConst(val); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); + return tmp; } -unsigned PETernary::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&use_expr_type, - bool&flag) +unsigned PETernary::test_width(Design*des, NetScope*scope, width_mode_t&mode) { - // The condition of the ternary is self-determined, but we - // test its width to force its type to be calculated. - ivl_variable_type_t con_type = IVL_VT_NO_TYPE; - bool con_flag = false; - expr_->test_width(des, scope, 0, 0, con_type, con_flag); + // The condition of the ternary is self-determined, so + // we will test its width when we elaborate it. - ivl_variable_type_t tru_type = IVL_VT_NO_TYPE; - unsigned tru_wid = tru_->test_width(des, scope, min, lval, tru_type,flag); + // Test the width of the true and false clauses. + unsigned tru_width = tru_->test_width(des, scope, mode); - bool initial_flag = flag; - ivl_variable_type_t fal_type = IVL_VT_NO_TYPE; - unsigned fal_wid = fal_->test_width(des, scope, min, lval, fal_type,flag); + width_mode_t saved_mode = mode; - // If the false clause is unsized, then try again with the - // true clause, because it might choose a different width if - // it is in an unsized context. - if (initial_flag == false && flag == true) { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "False clause is unsized, so retest width of true clause." - << endl; - tru_wid = tru_->test_width(des, scope, max(min,fal_wid), lval, tru_type, flag); + unsigned fal_width = fal_->test_width(des, scope, mode); + + // If the width mode changed, retest the true clause, as it + // may choose a different width if it is in an unsized context. + if ((mode >= LOSSLESS) && (saved_mode < LOSSLESS)) { + tru_width = tru_->test_width(des, scope, mode); } // If either of the alternatives is IVL_VT_REAL, then the @@ -3527,28 +3287,42 @@ unsigned PETernary::test_width(Design*des, NetScope*scope, // of the alternatives is IVL_VT_LOGIC, then the expression as // a whole is IVL_VT_LOGIC. The fallback assumes that the // types are the same and we take that. + ivl_variable_type_t tru_type = tru_->expr_type(); + ivl_variable_type_t fal_type = fal_->expr_type(); + if (tru_type == IVL_VT_REAL || fal_type == IVL_VT_REAL) { expr_type_ = IVL_VT_REAL; - expr_width_ = 1; } else if (tru_type == IVL_VT_LOGIC || fal_type == IVL_VT_LOGIC) { expr_type_ = IVL_VT_LOGIC; - expr_width_ = max(tru_wid,fal_wid); } else { ivl_assert(*this, tru_type == fal_type); expr_type_ = tru_type; - expr_width_ = max(tru_wid,fal_wid); + } + if (expr_type_ == IVL_VT_REAL) { + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = true; + } else { + expr_width_ = max(tru_width, fal_width); + min_width_ = max(tru_->min_width(), fal_->min_width()); + signed_flag_ = tru_->has_sign() && fal_->has_sign(); + + // If the alternatives are different types, the expression + // is forced to unsigned. In this case the lossless width + // calculation is unreliable and we need to make sure the + // final expression width is at least integer_width. + if ((mode == LOSSLESS) && (tru_->has_sign() != fal_->has_sign())) + mode = UNSIZED; } if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Ternary expression type=" << expr_type_ << ", width=" << expr_width_ - << ", unsized_flag=" << flag << " (tru_type=" << tru_type << ", fal_type=" << fal_type << ")" << endl; - use_expr_type = expr_type_; - return expr_width_; + return fix_width_(mode); } bool NetETernary::test_operand_compat(ivl_variable_type_t l, @@ -3576,26 +3350,12 @@ bool NetETernary::test_operand_compat(ivl_variable_type_t l, * methods. If any elaboration fails, then give up and return 0. */ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { assert(expr_); assert(tru_); assert(fal_); - int use_wid = expr_wid >= 0? expr_wid : 0; - - if (expr_wid < 0) { - use_wid = expr_width(); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Self-sized ternary chooses wid="<< use_wid - << ", type=" << expr_type() - << ", expr=" << *this - << endl; - - ivl_assert(*this, use_wid > 0); - } - // Elaborate and evaluate the condition expression. Note that // it is always self-determined. NetExpr*con = elab_and_eval(des, scope, expr_, -1); @@ -3620,16 +3380,8 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, cerr << get_fileline() << ": debug: Short-circuit " "elaborate TRUE clause of ternary." << endl; - if (use_wid <= 0) { - cerr << get_fileline() << ": internal error: " - << "Unexpected use_wid=" << use_wid - << " processing short-circuit TRUE clause" - << " of expression: " << *this << endl; - } - ivl_assert(*this, use_wid > 0); - NetExpr*texpr = elab_and_eval_alternative_(des, scope, tru_, - use_wid); - return pad_to_width(texpr, use_wid, *this); + + return elab_and_eval_alternative_(des, scope, tru_, expr_wid); } // Condition is constant FALSE, so we only need the @@ -3639,29 +3391,21 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, cerr << get_fileline() << ": debug: Short-circuit " "elaborate FALSE clause of ternary." << endl; - if (use_wid <= 0) { - cerr << get_fileline() << ": internal error: " - << "Unexpected use_wid=" << use_wid - << " processing short-circuit FALSE clause" - << " of expression: " << *this << endl; - } - ivl_assert(*this, use_wid > 0); - NetExpr*texpr = elab_and_eval_alternative_(des, scope, fal_, - use_wid); - return pad_to_width(texpr, use_wid, *this); + + return elab_and_eval_alternative_(des, scope, fal_, expr_wid); } // X and Z conditions need to blend both results, so we // can't short-circuit. } - NetExpr*tru = elab_and_eval_alternative_(des, scope, tru_, use_wid); + NetExpr*tru = elab_and_eval_alternative_(des, scope, tru_, expr_wid); if (tru == 0) { delete con; return 0; } - NetExpr*fal = elab_and_eval_alternative_(des, scope, fal_, use_wid); + NetExpr*fal = elab_and_eval_alternative_(des, scope, fal_, expr_wid); if (fal == 0) { delete con; delete tru; @@ -3677,15 +3421,7 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, return 0; } - suppress_binary_operand_sign_if_needed(tru, fal); - - /* Whatever the width we choose for the ternary operator, we - need to make sure the operands match. */ - tru = pad_to_width(tru, use_wid, *this); - fal = pad_to_width(fal, use_wid, *this); - - NetETernary*res = new NetETernary(con, tru, fal); - res->cast_signed(tru->has_sign() && fal->has_sign()); + NetETernary*res = new NetETernary(con, tru, fal, expr_wid, signed_flag_); res->set_line(*this); return res; } @@ -3697,19 +3433,24 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, * self-determined. */ NetExpr* PETernary::elab_and_eval_alternative_(Design*des, NetScope*scope, - PExpr*expr, int use_wid) const + PExpr*expr, unsigned expr_wid) const { + unsigned context_wid = expr_wid; if (type_is_vectorable(expr->expr_type()) && !type_is_vectorable(expr_type_)) { - return elab_and_eval(des, scope, expr, -1); + expr_wid = expr->expr_width(); + context_wid = -1; + } else { + expr->cast_signed(signed_flag_); } + NetExpr*tmp = expr->elaborate_expr(des, scope, expr_wid, false); + if (tmp == 0) return 0; - return elab_and_eval(des, scope, expr, use_wid); + eval_expr(tmp, context_wid); + + return tmp; } -unsigned PEUnary::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEUnary::test_width(Design*des, NetScope*scope, width_mode_t&mode) { switch (op_) { case '&': // Reduction AND @@ -3718,59 +3459,42 @@ unsigned PEUnary::test_width(Design*des, NetScope*scope, case 'A': // Reduction NAND (~&) case 'N': // Reduction NOR (~|) case 'X': // Reduction NXOR (~^) - { - ivl_variable_type_t sub_type = IVL_VT_NO_TYPE; - bool flag = false; - unsigned swid = expr_->test_width(des, scope, 0, 0, sub_type, flag); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Test width of sub-expression of " << op_ - << " returns " << swid << "." << endl; - - expr_type_ = sub_type; - } - expr_width_ = 1; - - expr_type__ = expr_type_; - return expr_width_; - case '!': - { - ivl_variable_type_t sub_type = IVL_VT_NO_TYPE; - bool flag = false; - expr_->test_width(des, scope, 0, 0, sub_type, flag); - expr_type_ = (sub_type==IVL_VT_BOOL)? IVL_VT_BOOL : IVL_VT_LOGIC; - } - // Logical ! always returns a single-bit LOGIC or BOOL value. - expr_width_ = 1; - expr_type__ = expr_type_; - return expr_width_; + { + width_mode_t sub_mode = SIZED; + unsigned sub_width = expr_->test_width(des, scope, sub_mode); + + expr_type_ = expr_->expr_type(); + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = false; + + if ((op_ == '!') && (expr_type_ != IVL_VT_BOOL)) + expr_type_ = IVL_VT_LOGIC; + + if (debug_elaborate) + cerr << get_fileline() << ": debug: " + << "Test width of sub-expression of " << op_ + << " returns " << sub_width << "." << endl; + + } + return expr_width_; } - unsigned test_wid = expr_->test_width(des, scope, min, lval, expr_type__, unsized_flag); - switch (op_) { - // For these operators, the act of padding to the - // minimum width can have an important impact on the - // calculation. So don't let the tested width be less - // then the minimum width. - case '-': - case '+': - case '~': - if (test_wid < min) - test_wid = min; - break; - } + expr_width_ = expr_->test_width(des, scope, mode); + expr_type_ = expr_->expr_type(); + min_width_ = expr_->min_width(); + signed_flag_ = expr_->has_sign(); - expr_type_ = expr_type__; - expr_width_ = test_wid; - return test_wid; + return fix_width_(mode); } NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { /* Reduction operators and ! always have a self determined width. */ + unsigned sub_width = expr_wid; switch (op_) { case '!': case '&': // Reduction AND @@ -3779,12 +3503,11 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, case 'A': // Reduction NAND (~&) case 'N': // Reduction NOR (~|) case 'X': // Reduction NXOR (~^) - expr_wid = -1; + sub_width = expr_->expr_width(); default: break; } - - NetExpr*ip = expr_->elaborate_expr(des, scope, expr_wid, false); + NetExpr*ip = expr_->elaborate_expr(des, scope, sub_width, false); if (ip == 0) return 0; ivl_assert(*expr_, expr_type_ != IVL_VT_NO_TYPE); @@ -3792,7 +3515,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, NetExpr*tmp; switch (op_) { default: - tmp = new NetEUnary(op_, ip); + tmp = new NetEUnary(op_, ip, expr_wid, signed_flag_); tmp->set_line(*this); break; @@ -3800,31 +3523,13 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, if (NetEConst*ipc = dynamic_cast(ip)) { verinum val = ipc->value(); - if (expr_wid > 0) - val = pad_to_width(val, expr_wid); - - /* When taking the - of a number, extend it one - bit to accommodate a possible sign bit. - - NOTE: This may not be correct! The test_width - is supposed to detect the special case that we - want to do lossless self-determined - expressions, and the function that calls - elaborate_expr should account for that in the - expr_wid argument. */ - unsigned use_len = val.len(); - if (expr_wid < 0) - use_len += 1; /* Calculate unary minus as 0-val */ - verinum zero (verinum::V0, use_len, val.has_len()); + verinum zero (verinum::V0, expr_wid); zero.has_sign(val.has_sign()); - verinum nval = zero - val; - - if (val.has_len()) - nval = verinum(nval, val.len()); - nval.has_sign(val.has_sign()); + verinum nval = verinum(zero - val, expr_wid); tmp = new NetEConst(nval); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); delete ip; @@ -3838,9 +3543,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, delete ip; } else { - if (expr_wid > 0) - ip = pad_to_width(ip, expr_wid, *this); - tmp = new NetEUnary(op_, ip); + tmp = new NetEUnary(op_, ip, expr_wid, signed_flag_); tmp->set_line(*this); } break; @@ -3898,6 +3601,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, } tmp->set_line(*this); } + tmp = pad_to_width(tmp, expr_wid, *this); break; case '&': // Reduction AND @@ -3915,6 +3619,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, } tmp = new NetEUReduce(op_, ip); tmp->set_line(*this); + tmp = pad_to_width(tmp, expr_wid, *this); break; case '~': @@ -3925,15 +3630,13 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, return tmp; } -NetExpr* PEUnary::elaborate_expr_bits_(NetExpr*operand, int expr_wid) const +NetExpr* PEUnary::elaborate_expr_bits_(NetExpr*operand, unsigned expr_wid) const { // Handle the special case that the operand is a // constant. Simply calculate the constant results of the // expression and return that. if (NetEConst*ctmp = dynamic_cast (operand)) { verinum value = ctmp->value(); - if (expr_wid > (int)value.len()) - value = pad_to_width(value, expr_wid); // The only operand that I know can get here is the // unary not (~). @@ -3946,14 +3649,16 @@ NetExpr* PEUnary::elaborate_expr_bits_(NetExpr*operand, int expr_wid) const return ctmp; } - if (expr_wid > (int)operand->expr_width()) - operand = pad_to_width(operand, expr_wid, *this); - - NetEUBits*tmp = new NetEUBits(op_, operand); + NetEUBits*tmp = new NetEUBits(op_, operand, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; } +NetExpr* PEVoid::elaborate_expr(Design*, NetScope*, unsigned, bool) const +{ + return 0; +} + NetNet* Design::find_discipline_reference(ivl_discipline_t dis, NetScope*scope) { NetNet*gnd = discipline_references_[dis->name()]; diff --git a/elab_lval.cc b/elab_lval.cc index 5104ac48d..0a933ddb0 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2011 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 @@ -255,12 +255,6 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, ivl_assert(*this, index_head.msb != 0); ivl_assert(*this, index_head.lsb == 0); - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_head.msb->test_width(des, scope, integer_width, integer_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 @@ -335,12 +329,6 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, NetNet*reg = lv->sig(); - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_tail.msb->test_width(des, scope, integer_width, integer_width, - 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. @@ -463,12 +451,6 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, unsigned long wid; calculate_up_do_width_(des, scope, wid); - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_tail.msb->test_width(des, scope, integer_width, integer_width, - expr_type_tmp, unsized_flag_tmp); - NetExpr*base = elab_and_eval(des, scope, index_tail.msb, -1); ivl_select_type_t sel_type = IVL_SEL_OTHER; diff --git a/elab_net.cc b/elab_net.cc index 05cbaf525..f2610be27 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -198,12 +198,6 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, case index_component_t::SEL_IDX_DO: case index_component_t::SEL_IDX_UP: { - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_tail.msb->test_width(des, scope, - integer_width, integer_width, - expr_type_tmp, unsized_flag_tmp); need_constant_expr = true; NetExpr*tmp_ex = elab_and_eval(des, scope, index_tail.msb, -1); need_constant_expr = false; @@ -477,12 +471,6 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, } ivl_assert(*this, index_head.sel == index_component_t::SEL_BIT); - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_head.msb->test_width(des, scope, - integer_width, integer_width, - expr_type_tmp, unsized_flag_tmp); need_constant_expr = true; NetExpr*tmp_ex = elab_and_eval(des, scope, index_head.msb, -1); need_constant_expr = false; diff --git a/elab_scope.cc b/elab_scope.cc index 98b271267..b15b5a429 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2011 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 @@ -61,7 +61,6 @@ static void collect_parm_item_(Design*des, NetScope*scope, perm_string name, tmp->high_open_flag = range->high_open_flag; if (range->low_expr) { - probe_expr_width(des, scope, range->low_expr); tmp->low_expr = elab_and_eval(des, scope, range->low_expr, -1); ivl_assert(*range->low_expr, tmp->low_expr); } else { @@ -78,7 +77,6 @@ static void collect_parm_item_(Design*des, NetScope*scope, perm_string name, tmp->high_expr = tmp->low_expr; } else if (range->high_expr) { - probe_expr_width(des, scope, range->high_expr); tmp->high_expr = elab_and_eval(des, scope, range->high_expr, -1); ivl_assert(*range->high_expr, tmp->high_expr); } else { @@ -136,8 +134,8 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope, { bool rc_flag; assert(enum_type->range->size() == 2); - NetExpr*msb_ex = enum_type->range->front()->elaborate_expr(des, scope, -2, false); - NetExpr*lsb_ex = enum_type->range->back() ->elaborate_expr(des, scope, -2, false); + NetExpr*msb_ex = elab_and_eval(des, scope, enum_type->range->front(), -1); + NetExpr*lsb_ex = elab_and_eval(des, scope, enum_type->range->back(), -1); long msb = 0; rc_flag = eval_as_long(msb, msb_ex); @@ -162,7 +160,6 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope, // There is an explicit value. elaborate/evaluate // the value and assign it to the enumeration name. NetExpr*val = elab_and_eval(des, scope, cur->parm, - use_enum->base_width(), use_enum->base_width()); NetEConst*val_const = dynamic_cast (val); if (val_const == 0) { @@ -557,7 +554,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) // The initial value for the genvar does not need (nor can it // use) the genvar itself, so we can evaluate this expression // the same way any other parameter value is evaluated. - probe_expr_width(des, container, loop_init); need_constant_expr = true; NetExpr*init_ex = elab_and_eval(des, container, loop_init, -1); need_constant_expr = false; @@ -624,7 +620,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) cerr << get_fileline() << ": debug: genvar init = " << genvar << endl; container->genvar_tmp = loop_index; container->genvar_tmp_val = genvar; - probe_expr_width(des, container, loop_test); need_constant_expr = true; NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1); need_constant_expr = false; @@ -673,7 +668,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) elaborate_subscope_(des, scope); // Calculate the step for the loop variable. - probe_expr_width(des, container, loop_step); need_constant_expr = true; NetExpr*step_ex = elab_and_eval(des, container, loop_step, -1); need_constant_expr = false; @@ -692,7 +686,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) container->genvar_tmp_val = genvar; delete step; delete test_ex; - probe_expr_width(des, container, loop_test); test_ex = elab_and_eval(des, container, loop_test, -1); test = dynamic_cast(test_ex); assert(test); @@ -707,7 +700,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else_flag) { - probe_expr_width(des, container, loop_test); need_constant_expr = true; NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1); need_constant_expr = false; @@ -800,7 +792,6 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else bool PGenerate::generate_scope_case_(Design*des, NetScope*container) { - probe_expr_width(des, container, loop_test); need_constant_expr = true; NetExpr*case_value_ex = elab_and_eval(des, container, loop_test, -1); need_constant_expr = false; @@ -833,7 +824,6 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container) bool match_flag = false; for (unsigned idx = 0 ; idx < item->item_test.size() && !match_flag ; idx +=1 ) { - probe_expr_width(des, container, item->item_test[idx]); need_constant_expr = true; NetExpr*item_value_ex = elab_and_eval(des, container, item->item_test[idx], -1); need_constant_expr = false; @@ -1206,8 +1196,6 @@ void PGModule::elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const */ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*sc) const { - if (msb_) probe_expr_width(des, sc, msb_); - if (lsb_) probe_expr_width(des, sc, lsb_); need_constant_expr = true; NetExpr*mse = msb_ ? elab_and_eval(des, sc, msb_, -1) : 0; NetExpr*lse = lsb_ ? elab_and_eval(des, sc, lsb_, -1) : 0; diff --git a/elab_sig.cc b/elab_sig.cc index 2e70cb4a4..6aaaca577 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2011 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 @@ -471,8 +471,6 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const case PTF_REG_S: if (return_type_.range) { ivl_assert(*this, return_type_.range->size() == 2); - probe_expr_width(des, scope, return_type_.range->at(0)); - probe_expr_width(des, scope, return_type_.range->at(1)); need_constant_expr = true; NetExpr*me = elab_and_eval(des, scope, @@ -545,8 +543,6 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const case PTF_ATOM2: case PTF_ATOM2_S: ivl_assert(*this, return_type_.range != 0); - probe_expr_width(des, scope, (*return_type_.range)[0]); - probe_expr_width(des, scope, (*return_type_.range)[1]); long use_wid; { need_constant_expr = true; @@ -852,7 +848,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const bool bad_lsb = false, bad_msb = false; /* If they exist get the port definition MSB and LSB */ if (port_set_ && port_msb_ != 0) { - probe_expr_width(des, scope, port_msb_); /* We do not currently support constant user function. */ need_constant_expr = true; NetExpr*texpr = elab_and_eval(des, scope, port_msb_, -1); @@ -870,7 +865,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const delete texpr; - probe_expr_width(des, scope, port_lsb_); /* We do not currently support constant user function. */ need_constant_expr = true; texpr = elab_and_eval(des, scope, port_lsb_, -1); @@ -898,7 +892,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const /* If they exist get the net/etc. definition MSB and LSB */ if (net_set_ && net_msb_ != 0 && !bad_msb && !bad_lsb) { - probe_expr_width(des, scope, net_msb_); /* We do not currently support constant user function. */ need_constant_expr = true; NetExpr*texpr = elab_and_eval(des, scope, net_msb_, -1); @@ -916,7 +909,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const delete texpr; - probe_expr_width(des, scope, net_lsb_); /* We do not currently support constant user function. */ need_constant_expr = true; texpr = elab_and_eval(des, scope, net_lsb_, -1); @@ -1011,9 +1003,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const if (lidx_ || ridx_) { assert(lidx_ && ridx_); - probe_expr_width(des, scope, lidx_); - probe_expr_width(des, scope, ridx_); - need_constant_expr = true; NetExpr*lexp = elab_and_eval(des, scope, lidx_, -1); NetExpr*rexp = elab_and_eval(des, scope, ridx_, -1); diff --git a/elaborate.cc b/elaborate.cc index 0ecc01b0f..f50259303 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -115,6 +115,7 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const cerr << get_fileline() << ": debug: PGAssign: elaborated r-value" << " width="<< rval->vector_width() << ", type="<< rval->data_type() + << ", signed="<< rval->get_signed() << ", expr=" << *rval_expr << endl; } @@ -227,11 +228,6 @@ unsigned PGBuiltin::calculate_array_count_(Design*des, NetScope*scope, gate. Figure out how many are desired. */ if (msb_) { need_constant_expr = true; - ivl_variable_type_t use_type; - bool flag = false; - msb_->test_width(des, scope, 0, 0, use_type, flag); - flag = false; - lsb_->test_width(des, scope, 0, 0, use_type, flag); NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1); NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1); need_constant_expr = false; @@ -820,12 +816,7 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const sig = lval_sigs[idx]; } else { - unsigned use_width = array_count * instance_width; - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool flag = false; - ex->test_width(des, scope, 0, use_width, tmp_type, flag); - NetExpr*tmp = elab_and_eval(des, scope, ex, - use_width, use_width); + NetExpr*tmp = elab_and_eval(des, scope, ex, -1); sig = tmp->synthesize(des, scope, tmp); delete tmp; } @@ -1332,13 +1323,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const port is actually empty on the inside. We assume in that case that the port is input. */ - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool flag = false; - pins[idx]->test_width(des, scope, 0, desired_vector_width, - tmp_type, flag); - NetExpr*tmp_expr = elab_and_eval(des, scope, pins[idx], - desired_vector_width, - desired_vector_width); + NetExpr*tmp_expr = elab_and_eval(des, scope, pins[idx], -1); if (tmp_expr == 0) { cerr << pins[idx]->get_fileline() << ": internal error: Port expression " @@ -1891,7 +1876,7 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const if (pins[idx] == 0) continue; - NetExpr*expr_tmp = elab_and_eval(des, scope, pins[idx], 1, 1); + NetExpr*expr_tmp = elab_and_eval(des, scope, pins[idx], 1); if (expr_tmp == 0) { cerr << "internal error: Expression too complicated " "for elaboration:" << *pins[idx] << endl; @@ -2046,7 +2031,6 @@ NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, */ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) { - probe_expr_width(des, scope, expr); NetExpr*dex = elab_and_eval(des, scope, expr, -1); /* Print a warning if we find default and `timescale based @@ -2101,12 +2085,11 @@ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) NetExpr*scal_val = new NetECReal(verireal(round)); scal_val->set_line(*expr); - dex = new NetEBMult('*', dex, scal_val); + dex = new NetEBMult('*', dex, scal_val, 1, true); dex->set_line(*expr); // Cast this part of the expression to an integer. - dex = new NetECast('i', dex); - dex->set_width(64); + dex = new NetECast('i', dex, 64, false); dex->set_line(*expr); // Now scale the integer value. @@ -2117,8 +2100,7 @@ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) scal_val = new NetEConst(verinum(scale, 64)); scal_val->set_line(*expr); - dex = new NetEBMult('*', dex, scal_val); - dex->set_width(64); + dex = new NetEBMult('*', dex, scal_val, 64, false); dex->set_line(*expr); } else { int shift = scope->time_unit() - des->get_precision(); @@ -2128,7 +2110,7 @@ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) NetExpr*scal_val = new NetEConst(verinum(scale, 64)); scal_val->set_line(*expr); - dex = new NetEBMult('*', dex, scal_val); + dex = new NetEBMult('*', dex, scal_val, 64, false); dex->set_line(*expr); } @@ -2176,17 +2158,6 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const if (delay || event_) { unsigned wid = count_lval_width(lv); - rv->set_width(wid); - rv = pad_to_width(rv, wid, *this); - - if (wid > rv->expr_width()) { - cerr << get_fileline() << ": error: Unable to match " - "expression width of " << rv->expr_width() << - " to l-value width of " << wid << "." << endl; - //XXXX delete rv; - return 0; - } - NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::REG, wid); tmp->local_flag(true); @@ -2276,22 +2247,6 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const return bl; } - /* Based on the specific type of the l-value, do cleanup - processing on the r-value. */ - if (rv->expr_type() == IVL_VT_REAL) { - - // The r-value is a real. Casting will happen in the - // code generator, so leave it. - - } else { - unsigned wid = count_lval_width(lv); - if (wid > rv->expr_width()) { - rv->set_width(wid); - rv = pad_to_width(rv, wid, *this); - } - ivl_assert(*this, rv->expr_width() >= wid); - } - if (lv->expr_type() == IVL_VT_BOOL && rv->expr_type() != IVL_VT_BOOL) { if (debug_elaborate) cerr << get_fileline() << ": debug: Cast expression to int2" << endl; @@ -2342,20 +2297,6 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const NetExpr*rv = elaborate_rval_(des, scope, count_lval_width(lv), lv->expr_type()); if (rv == 0) return 0; - /* Handle the (common) case that the r-value is a vector. This - includes just about everything but reals. In this case, we - need to pad the r-value to match the width of the l-value. - - If in this case the l-val is a variable (i.e., real) then - the width to pad to will be 0, so this code is harmless. */ - if (rv->expr_type() == IVL_VT_REAL) { - - } else { - unsigned wid = count_lval_width(lv); - rv->set_width(wid); - rv = pad_to_width(rv, wid, *this); - } - NetExpr*delay = 0; if (delay_ != 0) { assert(count_ == 0 && event_ == 0); @@ -2507,7 +2448,6 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const { assert(scope); - probe_expr_width(des, scope, expr_); NetExpr*expr = elab_and_eval(des, scope, expr_, -1); if (expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate this case" @@ -2562,7 +2502,6 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const NetExpr*gu = 0; NetProc*st = 0; assert(cur_expr); - probe_expr_width(des, scope, cur_expr); gu = elab_and_eval(des, scope, cur_expr, -1); if (cur->stat) @@ -2585,7 +2524,6 @@ NetProc* PCondit::elaborate(Design*des, NetScope*scope) const << " with conditional: " << *expr_ << endl; // Elaborate and try to evaluate the conditional expression. - probe_expr_width(des, scope, expr_); NetExpr*expr = elab_and_eval(des, scope, expr_, -1); if (expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate" @@ -2701,29 +2639,12 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const svectoreparms (parm_count); + perm_string name = peek_tail_name(path_); + for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { PExpr*ex = parm(idx); if (ex != 0) { - ivl_variable_type_t use_type; - bool flag = false; - int use_wid = ex->test_width(des,scope,0,0, use_type, flag); - if (debug_elaborate) - cerr << ex->get_fileline() << ": debug: " - << "Argument " << (idx+1) - << " of system task tests its width as " << use_wid - << ", type=" << use_type - << ", unsized_flag=" << flag << endl; - - // If the argument expression is unsized, then - // elaborate as self-determined *lossless* instead - // of sized. - if (flag==true) - use_wid = -2; - - eparms[idx] = ex->elaborate_expr(des, scope, use_wid, true); - if (eparms[idx]) - eval_expr(eparms[idx]); - + eparms[idx] = elab_sys_task_arg(des, scope, name, idx, ex); } else { eparms[idx] = 0; } @@ -2733,8 +2654,7 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const // $sdf_annotate system task. There will be nothing for $sdf // to annotate, and the user is intending to turn the behavior // off anyhow, so replace the system task invocation with a no-op. - if (gn_specify_blocks_flag == false - && peek_tail_name(path_) == "$sdf_annotate") { + if (gn_specify_blocks_flag == false && name == "$sdf_annotate") { cerr << get_fileline() << ": warning: Omitting $sdf_annotate() " << "since specify blocks are being omitted." << endl; @@ -2743,8 +2663,7 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const return noop; } - NetSTask*cur = new NetSTask(peek_tail_name(path_), def_sfunc_as_task, - eparms); + NetSTask*cur = new NetSTask(name, def_sfunc_as_task, eparms); cur->set_line(*this); return cur; } @@ -2871,11 +2790,6 @@ NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const des->errors += 1; continue; } - if (wid > rv->expr_width()) { - rv->set_width(wid); - rv = pad_to_width(rv, wid, *this); - } - ivl_assert(*this, rv->expr_width() >= wid); NetAssign*pr = new NetAssign(lv, rv); pr->set_line(*this); @@ -2988,9 +2902,6 @@ NetCAssign* PCAssign::elaborate(Design*des, NetScope*scope) const if (rexp == 0) return 0; - rexp->set_width(lwid); - rexp = pad_to_width(rexp, lwid, *this); - dev = new NetCAssign(lval, rexp); if (debug_elaborate) { @@ -3306,8 +3217,7 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope, } } - probe_expr_width(des, scope, expr_[idx]->expr()); - NetExpr*tmp = elab_and_eval(des, scope, expr_[idx]->expr(), 0); + NetExpr*tmp = elab_and_eval(des, scope, expr_[idx]->expr(), -1); if (tmp == 0) { expr_[idx]->dump(cerr); cerr << endl; @@ -3409,8 +3319,9 @@ NetProc* PEventStatement::elaborate_wait(Design*des, NetScope*scope, /* Elaborate wait expression. Don't eval yet, we will do that shortly, after we apply a reduction or. */ - probe_expr_width(des, scope, pe); - NetExpr*expr = pe->elaborate_expr(des, scope, -1, false); + PExpr::width_mode_t mode; + pe->test_width(des, scope, mode); + NetExpr*expr = pe->elaborate_expr(des, scope, pe->expr_width(), false); if (expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate" " wait condition expression." << endl; @@ -3607,9 +3518,6 @@ NetForce* PForce::elaborate(Design*des, NetScope*scope) const if (rexp == 0) return 0; - rexp->set_width(lwid, true); - rexp = pad_to_width(rexp, lwid, *this); - if (ltype==IVL_VT_BOOL && rexp->expr_type()!=IVL_VT_BOOL) { if (debug_elaborate) { cerr << get_fileline() << ": debug: " @@ -3670,32 +3578,14 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const assert(sig); NetAssign_*lv = new NetAssign_(sig); - /* Calculate the width of the initialization as if this were - any other assignment statement. */ - unsigned use_width = lv->lwidth(); - bool unsized_flag = false; - ivl_variable_type_t expr1_type = IVL_VT_NO_TYPE; - use_width = expr1_->test_width(des, scope, use_width, use_width, expr1_type, unsized_flag); - /* Make the r-value of the initial assignment, and size it properly. Then use it to build the assignment statement. */ - etmp = elab_and_eval(des, scope, expr1_, use_width); - etmp->set_width(use_width); - etmp = pad_to_width(etmp, use_width, *this); + etmp = elaborate_rval_expr(des, scope, lv->expr_type(), lv->lwidth(), + expr1_); if (debug_elaborate) { cerr << get_fileline() << ": debug: FOR initial assign: " << sig->name() << " = " << *etmp << endl; - assert(etmp->expr_width() >= lv->lwidth()); - } - - /* Based on the specific type of the l-value, do cleanup - processing on the r-value. */ - if (etmp->expr_type() != IVL_VT_REAL) { - unsigned wid = count_lval_width(lv); - etmp->set_width(wid); - etmp = pad_to_width(etmp, wid, *this); - assert(etmp->expr_width() >= wid); } NetAssign*init = new NetAssign(lv, etmp); @@ -3729,9 +3619,16 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const assert(sig); lv = new NetAssign_(sig); - /* Make the rvalue of the increment expression, and size it - for the lvalue. */ - etmp = elab_and_eval(des, scope, expr2_, lv->lwidth()); + /* Make the r-value of the increment assignment, and size it + properly. Then use it to build the assignment statement. */ + etmp = elaborate_rval_expr(des, scope, lv->expr_type(), lv->lwidth(), + expr2_); + + if (debug_elaborate) { + cerr << get_fileline() << ": debug: FOR increment assign: " + << sig->name() << " = " << *etmp << endl; + } + NetAssign*step = new NetAssign(lv, etmp); step->set_line(*this); @@ -3741,7 +3638,6 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const /* Elaborate the condition expression. Try to evaluate it too, in case it is a constant. This is an interesting case worthy of a warning. */ - probe_expr_width(des, scope, cond_); NetExpr*ce = elab_and_eval(des, scope, cond_, -1); if (ce == 0) { delete top; @@ -3832,7 +3728,6 @@ NetProc* PRepeat::elaborate(Design*des, NetScope*scope) const { assert(scope); - probe_expr_width(des, scope, expr_); NetExpr*expr = elab_and_eval(des, scope, expr_, -1); if (expr == 0) { cerr << get_fileline() << ": Unable to elaborate" @@ -3956,7 +3851,6 @@ NetProc* PTrigger::elaborate(Design*des, NetScope*scope) const */ NetProc* PWhile::elaborate(Design*des, NetScope*scope) const { - probe_expr_width(des, scope, cond_); NetExpr*tmp = elab_and_eval(des, scope, cond_, -1); NetWhile*loop = new NetWhile(tmp, statement_->elaborate(des, scope)); return loop; @@ -4059,8 +3953,7 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const them for the timescale/precision of the scope. */ for (unsigned idx = 0 ; idx < ndelays ; idx += 1) { PExpr*exp = delays[idx]; - probe_expr_width(des, scope, exp); - NetExpr*cur = elab_and_eval(des, scope, exp, 0); + NetExpr*cur = elab_and_eval(des, scope, exp, -1); if (NetEConst*con = dynamic_cast (cur)) { verinum fn = con->value(); @@ -4098,7 +3991,6 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const NetNet*condit_sig = 0; if (conditional && condition) { - probe_expr_width(des, scope, condition); NetExpr*tmp = elab_and_eval(des, scope, condition, -1); ivl_assert(*condition, tmp); @@ -4255,7 +4147,6 @@ bool Module::elaborate(Design*des, NetScope*scope) const for (specparam_it_t cur = specparams.begin() ; cur != specparams.end() ; ++ cur ) { - probe_expr_width(des, scope, (*cur).second); need_constant_expr = true; NetExpr*val = elab_and_eval(des, scope, (*cur).second, -1); need_constant_expr = false; diff --git a/elaborate_analog.cc b/elaborate_analog.cc index 5d538b98d..25cbdd4e6 100644 --- a/elaborate_analog.cc +++ b/elaborate_analog.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Stephen Williams (steve@icarus.com) + * Copyright (c) 2008,2011 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 @@ -28,8 +28,6 @@ NetProc* AContrib::elaborate(Design*des, NetScope*scope) const { - probe_expr_width(des, scope, lval_); - probe_expr_width(des, scope, rval_); NetExpr*lval = elab_and_eval(des, scope, lval_, -1); NetExpr*rval = elab_and_eval(des, scope, rval_, -1); diff --git a/eval_tree.cc b/eval_tree.cc index bb37e1401..10e359a8e 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2011 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 @@ -29,7 +29,7 @@ # include "ivl_assert.h" # include "netmisc.h" -NetExpr* NetExpr::eval_tree(int) +NetExpr* NetExpr::eval_tree() { return 0; } @@ -114,12 +114,12 @@ NetECReal* NetEBAdd::eval_tree_real_() return res; } -NetExpr* NetEBAdd::eval_tree(int prune_to_width) +NetExpr* NetEBAdd::eval_tree() { - eval_expr(left_, prune_to_width); - eval_expr(right_, prune_to_width); + eval_expr(left_); + eval_expr(right_); - if (left_->expr_type() == IVL_VT_REAL || right_->expr_type()==IVL_VT_REAL) + if (expr_type() == IVL_VT_REAL) return eval_tree_real_(); NetEConst*lc = dynamic_cast(left_); @@ -131,33 +131,23 @@ NetExpr* NetEBAdd::eval_tree(int prune_to_width) verinum lval = lc->value(); verinum rval = rc->value(); + unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + ivl_assert(*this, rval.len() == wid); + verinum val; switch (op_) { case '+': - val = lval + rval; + val = verinum(lval + rval, wid); break; case '-': - val = lval - rval; + val = verinum(lval - rval, wid); break; default: return 0; } - /* Result might have known width. */ - if (has_width()) { - unsigned lwid = lc->expr_width(); - unsigned rwid = rc->expr_width(); - unsigned wid = (rwid > lwid) ? rwid : lwid; - if (prune_to_width < 0) - wid += 1; - verinum val2=verinum(val,wid); - val=val2; - } else { - /* No fixed width, so trim the bits losslessly. */ - verinum val2 = trim_vnum(val); - val = val2; - } - NetEConst *res = new NetEConst(val); ivl_assert(*this, res); res->set_line(*this); @@ -188,37 +178,20 @@ NetExpr* NetEBAdd::eval_tree(int prune_to_width) verinum lval = lc->value(); verinum rval = rc->value(); - if (lval.len() < expr_width()) - lval = pad_to_width(lval, expr_width()); - if (rval.len() < expr_width()) - rval = pad_to_width(rval, expr_width()); - - if (se->expr_width() > this->expr_width()) { - cerr << get_fileline() << ": internal error: " - << "expr_width()=" << expr_width() - << ", sub expr_width()=" << se->expr_width() - << ", sub expression=" << *se << endl; - } - ivl_assert(*this, se->expr_width() <= this->expr_width()); + unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + ivl_assert(*this, rval.len() == wid); verinum val; if (op_ == se->op_) { /* (a + lval) + rval --> a + (rval+lval) */ /* (a - lval) - rval --> a - (rval+lval) */ - val = rval + lval; + val = verinum(rval + lval, wid); } else { /* (a - lval) + rval --> a + (rval-lval) */ /* (a + lval) - rval --> a - (rval-lval) */ - val = rval - lval; - } - - // Since we padded the operands above to be the minimum - // width, the val should also be at least expr_width(). - ivl_assert(*this, val.len() >= expr_width()); - if (val.len() > expr_width()) { - verinum tmp (val, expr_width()); - tmp.has_sign(val.has_sign()); - val = tmp; + val = verinum(rval - lval, wid); } NetEConst*tmp = new NetEConst(val); @@ -236,15 +209,15 @@ NetExpr* NetEBAdd::eval_tree(int prune_to_width) return 0; } -NetEConst* NetEBBits::eval_tree(int prune_to_width) +NetEConst* NetEBBits::eval_tree() { if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluating expression:" - << *this << ", prune_to_width=" << prune_to_width << endl; + << *this << endl; } - eval_expr(left_, prune_to_width); - eval_expr(right_, prune_to_width); + eval_expr(left_); + eval_expr(right_); NetEConst*lc = dynamic_cast(left_); NetEConst*rc = dynamic_cast(right_); @@ -266,79 +239,33 @@ NetEConst* NetEBBits::eval_tree(int prune_to_width) verinum lval = lc->value(); verinum rval = rc->value(); - unsigned lwid = lc->expr_width(); - if (lwid == 0) lwid = lval.len(); - - unsigned rwid = rc->expr_width(); - if (rwid == 0) rwid = rval.len(); - unsigned wid = expr_width(); - if (wid == 0) - wid = (rwid > lwid)? rwid : lwid; + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + ivl_assert(*this, rval.len() == wid); verinum res (verinum::V0, wid); - if (lwid > wid) - lwid = wid; - if (rwid > wid) - rwid = wid; - - // Sub-expressions of bitwise operators need to be the same - // width. Pad them out if necessary. - if (lwid < wid) { - lval = pad_to_width(lval, wid); - lwid = wid; - } - if (rwid < wid) { - rval = pad_to_width(rval, wid); - rwid = wid; - } - switch (op()) { case '|': { - unsigned cnt = lwid; - if (cnt > wid) cnt = wid; - if (cnt > rwid) cnt = rwid; - for (unsigned idx = 0 ; idx < cnt ; idx += 1) + for (unsigned idx = 0 ; idx < wid ; idx += 1) res.set(idx, lval.get(idx) | rval.get(idx)); - if (lwid < rwid) - for (unsigned idx = lwid ; idx < rwid ; idx += 1) - res.set(idx, rval.get(idx)); - - if (rwid < lwid) - for (unsigned idx = rwid ; idx < lwid ; idx += 1) - res.set(idx, lval.get(idx)); - break; } case '&': { - unsigned cnt = lwid; - if (cnt > wid) cnt = wid; - if (cnt > rwid) cnt = rwid; - for (unsigned idx = 0 ; idx < cnt ; idx += 1) + for (unsigned idx = 0 ; idx < wid ; idx += 1) res.set(idx, lval.get(idx) & rval.get(idx)); break; } case '^': { - unsigned cnt = lwid; - if (cnt > wid) cnt = wid; - if (cnt > rwid) cnt = rwid; - for (unsigned idx = 0 ; idx < cnt ; idx += 1) + for (unsigned idx = 0 ; idx < wid ; idx += 1) res.set(idx, lval.get(idx) ^ rval.get(idx)); - if (lwid < rwid) - for (unsigned idx = lwid ; idx < rwid ; idx += 1) - res.set(idx, rval.get(idx)); - - if (rwid < lwid) - for (unsigned idx = rwid ; idx < lwid ; idx += 1) - res.set(idx, lval.get(idx)); - break; } @@ -761,7 +688,7 @@ NetEConst* NetEBComp::eval_eqeqeq_(bool ne_flag) return result; } -NetEConst* NetEBComp::eval_tree(int) +NetEConst* NetEBComp::eval_tree() { eval_expr(left_); eval_expr(right_); @@ -850,11 +777,8 @@ NetExpr* NetEBDiv::eval_tree_real_() * The NetEBDiv operator includes the / and % operators. First evaluate * the sub-expressions, then perform the required operation. */ -NetExpr* NetEBDiv::eval_tree(int prune_to_width) +NetExpr* NetEBDiv::eval_tree() { -// assert(prune_to_width <= 0); -// HERE - eval_expr(left_); eval_expr(right_); @@ -865,20 +789,26 @@ NetExpr* NetEBDiv::eval_tree(int prune_to_width) NetEConst*rc = dynamic_cast(right_); if (lc == 0 || rc == 0) return 0; - // Make sure the expression is evaluated at the - // expression width. - verinum lval = pad_to_width(lc->value(), expr_width()); - verinum rval = pad_to_width(rc->value(), expr_width()); + verinum lval = lc->value(); + verinum rval = rc->value(); - NetExpr*tmp = 0; + unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + ivl_assert(*this, rval.len() == wid); + + verinum val; switch (op_) { case '/': - tmp = new NetEConst(lval / rval); + val = verinum(lval / rval, wid); break; case '%': - tmp = new NetEConst(lval % rval); + val = verinum(lval % rval, wid); break; + default: + return 0; } + NetExpr*tmp = new NetEConst(val); ivl_assert(*this, tmp); tmp->set_line(*this); @@ -928,7 +858,7 @@ NetEConst* NetEBLogic::eval_tree_real_() return tmp; } -NetEConst* NetEBLogic::eval_tree(int) +NetEConst* NetEBLogic::eval_tree() { eval_expr(left_); eval_expr(right_); @@ -1024,11 +954,8 @@ NetExpr* NetEBMult::eval_tree_real_() return res; } -NetExpr* NetEBMult::eval_tree(int prune_to_width) +NetExpr* NetEBMult::eval_tree() { -// assert(prune_to_width <= 0); -// HERE - eval_expr(left_); eval_expr(right_); @@ -1042,7 +969,13 @@ NetExpr* NetEBMult::eval_tree(int prune_to_width) verinum lval = lc->value(); verinum rval = rc->value(); - NetEConst*tmp = new NetEConst(lval * rval); + unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + ivl_assert(*this, rval.len() == wid); + + verinum val(lval * rval, wid); + NetEConst*tmp = new NetEConst(val); ivl_assert(*this, tmp); tmp->set_line(*this); @@ -1072,11 +1005,8 @@ NetExpr* NetEBPow::eval_tree_real_() return res; } -NetExpr* NetEBPow::eval_tree(int prune_to_width) +NetExpr* NetEBPow::eval_tree() { -// assert(prune_to_width <= 0); -// HERE - eval_expr(left_); eval_expr(right_); @@ -1090,7 +1020,12 @@ NetExpr* NetEBPow::eval_tree(int prune_to_width) verinum lval = lc->value(); verinum rval = rc->value(); - NetEConst*res = new NetEConst( pow(lval,rval) ); + unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + + verinum val(pow(lval, rval), wid); + NetEConst*res = new NetEConst(val); ivl_assert(*this, res); res->set_line(*this); @@ -1105,7 +1040,7 @@ NetExpr* NetEBPow::eval_tree(int prune_to_width) * Evaluate the shift operator if possible. For this to work, both * operands must be constant. */ -NetEConst* NetEBShift::eval_tree(int prune_to_width) +NetEConst* NetEBShift::eval_tree() { eval_expr(left_); eval_expr(right_); @@ -1116,79 +1051,44 @@ NetEConst* NetEBShift::eval_tree(int prune_to_width) NetEConst*res; - verinum rv = re->value(); verinum lv = le->value(); + verinum rv = re->value(); - /* Make an early estimate of the expression width. */ unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lv.len() == wid); if (rv.is_defined()) { - unsigned shift = rv.as_ulong(); if (debug_eval_tree) { cerr << get_fileline() << ": debug: " << "Evaluate " << lv << "<<" << op() << ">> " - << rv << ", wid=" << wid << ", shift=" << shift - << ", lv.has_len()=" << lv.has_len() << endl; + << rv << ", wid=" << wid << ", shift=" << shift << endl; } - if ((wid == 0) || ! lv.has_len()) { - /* If the caller doesn't care what the width is, - then calculate a width from the trimmed left - expression, plus the shift. This avoids - data loss. */ - lv = trim_vnum(lv); - wid = lv.len(); - if (op() == 'l') - wid = lv.len() + shift; + verinum val; + switch (op_) { + case 'l': + val = verinum(lv << shift, wid); + break; + case 'r': + lv.has_sign(false); + val = verinum(lv >> shift, wid); + break; + case 'R': + lv.has_sign(true); + val = verinum(lv >> shift, wid); + break; + default: + return 0; } + val.has_sign(has_sign()); - if (prune_to_width > 0 && wid > (unsigned)prune_to_width) - wid = prune_to_width; - - assert(wid > 0); - verinum::V pad = verinum::V0; - if (op() == 'R' && has_sign()) { - pad = lv[lv.len()-1]; - } - verinum nv (pad, wid, lv.has_len()); - - if (op() == 'r' || op() == 'R') { - unsigned cnt = wid; - if (cnt > nv.len()) - cnt = nv.len(); - if (shift >= lv.len()) - cnt = 0; - else if (cnt > (lv.len()-shift)) - cnt = (lv.len()-shift); - for (unsigned idx = 0 ; idx < cnt ; idx += 1) - nv.set(idx, lv[idx+shift]); - - } else { - unsigned cnt = wid; - if (cnt > lv.len()) - cnt = lv.len(); - if (shift >= nv.len()) - cnt = 0; - else if (cnt > (nv.len()-shift)) - cnt = nv.len() - shift; - - for (unsigned idx = 0 ; idx < cnt ; idx += 1) - nv.set(idx+shift, lv[idx]); - } - - res = new NetEConst(nv); - + res = new NetEConst(val); } else { - if (wid == 0) wid = left_->expr_width(); - - if (prune_to_width > 0 && wid > (unsigned)prune_to_width) - wid = prune_to_width; - - assert(wid > 0); - verinum nv (verinum::Vx, wid); - res = new NetEConst(nv); + verinum val (verinum::Vx, wid); + res = new NetEConst(val); } res->set_line(*this); @@ -1196,7 +1096,7 @@ NetEConst* NetEBShift::eval_tree(int prune_to_width) return res; } -NetEConst* NetEConcat::eval_tree(int prune_to_width) +NetEConst* NetEConcat::eval_tree() { // HERE unsigned repeat_val = repeat(); @@ -1204,7 +1104,7 @@ NetEConst* NetEConcat::eval_tree(int prune_to_width) if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluating expression:" - << *this << ", prune_to_width=" << prune_to_width << endl; + << *this << endl; } unsigned gap = 0; @@ -1225,7 +1125,7 @@ NetEConst* NetEConcat::eval_tree(int prune_to_width) // that is here. If I succeed, reset the parameter to // the evaluated value. assert(parms_[idx]); - NetExpr*expr = parms_[idx]->eval_tree(0); + NetExpr*expr = parms_[idx]->eval_tree(); if (expr) { expr->set_line(*parms_[idx]); delete parms_[idx]; @@ -1285,16 +1185,15 @@ NetEConst* NetEConcat::eval_tree(int prune_to_width) val.has_sign( this->has_sign() ); NetEConst*res = new NetEConst(val); - res->set_width(val.len()); return res; } -NetEConst* NetESelect::eval_tree(int prune_to_width) +NetEConst* NetESelect::eval_tree() { // HERE if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluating expression:" - << *this << ", prune_to_width=" << prune_to_width << endl; + << *this << endl; } eval_expr(expr_); @@ -1318,14 +1217,12 @@ NetEConst* NetESelect::eval_tree(int prune_to_width) verinum::V pad_bit = verinum::Vx; if (base_ == 0) { - /* If the base is NULL (different from 0) the this - select is here for sign extension. So calculate a - proper pad bit. Extend x or z or 0, and sign extend 1 - if this is signed. */ - unsigned top = expr->expr_width()-1; - - pad_bit = eval.get(top); - if (pad_bit==verinum::V1 && !has_sign()) + /* If the base is NULL (different from 0) then this + select is here for zero or sign extension. So + calculate a proper pad bit. */ + if (has_sign()) + pad_bit = eval.get(expr->expr_width()-1); + else pad_bit = verinum::V0; } @@ -1365,12 +1262,12 @@ static void print_ternary_cond(NetExpr*expr) * evaluates to x or z, then merge the constant bits of the true and * false expressions. */ -NetExpr* NetETernary::eval_tree(int prune_to_width) +NetExpr* NetETernary::eval_tree() { eval_expr(cond_); switch (const_logical(cond_)) { case C_0: - eval_expr(false_val_, prune_to_width); + eval_expr(false_val_); if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluate ternary with " @@ -1393,7 +1290,7 @@ NetExpr* NetETernary::eval_tree(int prune_to_width) return false_val_->dup_expr(); case C_1: - eval_expr(true_val_, prune_to_width); + eval_expr(true_val_); if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluate ternary with " << "constant condition value: "; @@ -1425,8 +1322,8 @@ NetExpr* NetETernary::eval_tree(int prune_to_width) expressions down to constants then compare the values to build up a constant result. */ - eval_expr(true_val_, prune_to_width); - eval_expr(false_val_, prune_to_width); + eval_expr(true_val_); + eval_expr(false_val_); NetEConst*t = dynamic_cast(true_val_); NetEConst*f = dynamic_cast(false_val_); @@ -1509,7 +1406,7 @@ NetExpr* NetEUnary::eval_tree_real_() return res; } -NetExpr* NetEUnary::eval_tree(int prune_to_width) +NetExpr* NetEUnary::eval_tree() { eval_expr(expr_); if (expr_type() == IVL_VT_REAL) return eval_tree_real_(); @@ -1529,7 +1426,7 @@ NetExpr* NetEUnary::eval_tree(int prune_to_width) if (val.is_defined()) { verinum tmp (verinum::V0, val.len()); tmp.has_sign(val.has_sign()); - val = tmp - val; + val = verinum(tmp - val, val.len()); } else { for (unsigned idx = 0 ; idx < val.len() ; idx += 1) val.set(idx, verinum::Vx); @@ -1572,9 +1469,9 @@ NetExpr* NetEUnary::eval_tree(int prune_to_width) } -NetExpr* NetEUBits::eval_tree(int prune_to_width) +NetExpr* NetEUBits::eval_tree() { - return NetEUnary::eval_tree(prune_to_width); + return NetEUnary::eval_tree(); } NetEConst* NetEUReduce::eval_tree_real_() @@ -1598,7 +1495,7 @@ NetEConst* NetEUReduce::eval_tree_real_() return tmp; } -NetEConst* NetEUReduce::eval_tree(int) +NetEConst* NetEUReduce::eval_tree() { eval_expr(expr_); if (expr_type() == IVL_VT_REAL) return eval_tree_real_(); @@ -1955,9 +1852,8 @@ static NetExpr* evaluate_min_max(NetExpr*&arg0_, NetExpr*&arg1_, return res; } -NetExpr* NetESFunc::eval_tree(int prune_to_width) +NetExpr* NetESFunc::eval_tree() { -// assert(prune_to_width <= 0); // HERE /* If we are not targeting at least Verilog-2005, Verilog-AMS * or using the Icarus misc flag then we do not support these @@ -2056,7 +1952,7 @@ NetExpr* NetESFunc::eval_tree(int prune_to_width) return rtn; } -NetExpr* NetEUFunc::eval_tree(int) +NetExpr* NetEUFunc::eval_tree() { if (need_constant_expr) { cerr << get_fileline() << ": sorry: constant user " diff --git a/net_design.cc b/net_design.cc index 7dab97b6f..9bd4d1a69 100644 --- a/net_design.cc +++ b/net_design.cc @@ -332,8 +332,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) /* Evaluate the msb expression, if it is present. */ PExpr*msb_expr = (*cur).second.msb_expr; if (msb_expr) { - probe_expr_width(des, this, msb_expr); - (*cur).second.msb = elab_and_eval(des, this, msb_expr, -2); + (*cur).second.msb = elab_and_eval(des, this, msb_expr, -1); if (! eval_as_long(msb, (*cur).second.msb)) { cerr << (*cur).second.val->get_fileline() << ": error: Unable to evaluate msb expression " @@ -349,8 +348,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) /* Evaluate the lsb expression, if it is present. */ PExpr*lsb_expr = (*cur).second.lsb_expr; if (lsb_expr) { - probe_expr_width(des, this, lsb_expr); - (*cur).second.lsb = elab_and_eval(des, this, lsb_expr, -2); + (*cur).second.lsb = elab_and_eval(des, this, lsb_expr, -1); if (! eval_as_long(lsb, (*cur).second.lsb)) { cerr << (*cur).second.val->get_fileline() << ": error: Unable to evaluate lsb expression " @@ -367,25 +365,11 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) PExpr*val_expr = (*cur).second.val_expr; NetScope*val_scope = (*cur).second.val_scope; - unsigned lval_wid = 0; + int lv_width = -2; if (range_flag) - lval_wid = (msb >= lsb) ? 1 + msb - lsb : 1 + lsb - msb; + lv_width = (msb >= lsb) ? 1 + msb - lsb : 1 + lsb - msb; - bool unsized_flag = false; - ivl_variable_type_t rval_type = IVL_VT_NO_TYPE; - int expr_wid = val_expr->test_width(des, val_scope, lval_wid, lval_wid, - rval_type, unsized_flag); - - if (unsized_flag && !range_flag) - expr_wid = -1; - - int prune_wid = -1; - if (gn_strict_expr_width_flag) - prune_wid = 0; - if (range_flag) - prune_wid = lval_wid; - - NetExpr*expr = elab_and_eval(des, val_scope, val_expr, expr_wid, prune_wid); + NetExpr*expr = elab_and_eval(des, val_scope, val_expr, lv_width); if (! expr) return; @@ -427,7 +411,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) if (range_flag) { /* If we have a real value convert it to an integer. */ if(NetECReal*tmp = dynamic_cast(expr)) { - verinum nval(tmp->value().as_long64(), lval_wid); + verinum nval(tmp->value().as_long64(), (unsigned)lv_width); expr = new NetEConst(nval); expr->set_line(*((*cur).second.val)); (*cur).second.val = expr; @@ -504,18 +488,7 @@ void NetScope::evaluate_parameter_real_(Design*des, param_ref_t cur) PExpr*val_expr = (*cur).second.val_expr; NetScope*val_scope = (*cur).second.val_scope; - bool unsized_flag = false; - ivl_variable_type_t rval_type = IVL_VT_NO_TYPE; - int expr_wid = val_expr->test_width(des, val_scope, 0, 0, - rval_type, unsized_flag); - if (unsized_flag) - expr_wid = -1; - - int prune_wid = -1; - if (gn_strict_expr_width_flag) - prune_wid = 0; - - NetExpr*expr = elab_and_eval(des, val_scope, val_expr, expr_wid, prune_wid); + NetExpr*expr = elab_and_eval(des, val_scope, val_expr, -1); if (! expr) return; diff --git a/net_expr.cc b/net_expr.cc index f54da9ea2..b632693fa 100644 --- a/net_expr.cc +++ b/net_expr.cc @@ -38,92 +38,17 @@ netenum_t*NetExpr::enumeration() const } /* - * Create an add/sub node from the two operands. Make a best guess of - * the + * Create an add/sub node from the two operands. */ -NetEBAdd::NetEBAdd(char op__, NetExpr*l, NetExpr*r, bool lossless_flag) -: NetEBinary(op__, l, r) +NetEBAdd::NetEBAdd(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - NetEConst* tmp; - - /* Catch the special case that one of the operands is an - unsized constant number. If so, then we should set the - width of that number to the size of the other operand, plus - one. This expands the expression to account for the largest - possible result. - - Remember to handle the special case of an unsized constant, - which we define to be at least "integer_width" bits. - - The set_width applied to a constant value will only - truncate the constant so far as it can still hold its - logical value, so this is safe to do. */ - if ( (tmp = dynamic_cast(r)) - && (! tmp->has_width()) - && (tmp->expr_width() > l->expr_width() || integer_width > l->expr_width()) ) { - - verinum tmp_v = trim_vnum(tmp->value()); - unsigned target_width = l->expr_width(); - if (target_width < tmp_v.len()) - target_width = tmp_v.len(); - if (lossless_flag) - target_width += 1; - if (target_width < integer_width) - target_width = integer_width; - - r->set_width(target_width); - - /* Note: This constant value will not gain a defined - width from this. Make sure. */ - assert(! r->has_width() ); - - expr_width(target_width); - - } else if ( (tmp = dynamic_cast(l)) - && (! tmp->has_width()) - && (tmp->expr_width() > r->expr_width() || integer_width > r->expr_width()) ) { - - verinum tmp_v = trim_vnum(tmp->value()); - unsigned target_width = r->expr_width(); - if (target_width < tmp_v.len()) - target_width = tmp_v.len(); - if (lossless_flag) - target_width += 1; - if (target_width < integer_width) - target_width = integer_width; - - l->set_width(target_width); - - /* Note: This constant value will not gain a defined - width from this. Make sure. */ - assert(! l->has_width() ); - - expr_width(target_width); - - } else if (r->expr_width() > l->expr_width()) { - unsigned loss_pad = lossless_flag? 1 : 0; - expr_width(r->expr_width() + loss_pad); - - } else { - unsigned loss_pad = lossless_flag? 1 : 0; - expr_width(l->expr_width() + loss_pad); - } - - cast_signed(l->has_sign() && r->has_sign()); } NetEBAdd::~NetEBAdd() { } -NetEBAdd* NetEBAdd::dup_expr() const -{ - NetEBAdd*result = new NetEBAdd(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - ivl_variable_type_t NetEBAdd::expr_type() const { if (left_->expr_type() == IVL_VT_REAL) @@ -137,50 +62,10 @@ ivl_variable_type_t NetEBAdd::expr_type() const /* * Create a comparison operator with two sub-expressions. - * - * Handle the special case of an unsized constant on the left or right - * side by resizing the number to match the other - * expression. Otherwise, the netlist will have to allow the - * expressions to have different widths. */ NetEBComp::NetEBComp(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +: NetEBinary(op__, l, r, 1, false) { - // The output of compare is always unsigned. - cast_signed_base_(false); - - if (NetEConst*tmp = dynamic_cast(r)) do { - - if (tmp->has_width()) - break; - - if (l->expr_width() == 0) - break; - - if (tmp->expr_width() == l->expr_width()) - break; - - tmp->set_width(l->expr_width()); - - } while (0); - - if (NetEConst*tmp = dynamic_cast(l)) do { - - if (tmp->has_width()) - break; - - if (r->expr_width() == 0) - break; - - if (tmp->expr_width() == r->expr_width()) - break; - - tmp->set_width(r->expr_width()); - - } while (0); - - - expr_width(1); } NetEBComp::~NetEBComp() @@ -207,29 +92,15 @@ ivl_variable_type_t NetEBComp::expr_type() const return IVL_VT_BOOL; } -NetEBDiv::NetEBDiv(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBDiv::NetEBDiv(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - unsigned w = l->expr_width(); - if (r->expr_width() > w) - w = r->expr_width(); - - expr_width(w); - cast_signed(l->has_sign() && r->has_sign()); } NetEBDiv::~NetEBDiv() { } -NetEBDiv* NetEBDiv::dup_expr() const -{ - NetEBDiv*result = new NetEBDiv(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - ivl_variable_type_t NetEBDiv::expr_type() const { if (left_->expr_type() == IVL_VT_REAL) @@ -241,11 +112,9 @@ ivl_variable_type_t NetEBDiv::expr_type() const return IVL_VT_LOGIC; } -NetEBMinMax::NetEBMinMax(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBMinMax::NetEBMinMax(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - expr_width( max(l->expr_width(), r->expr_width()) ); - cast_signed(l->has_sign() || r->has_sign()); } NetEBMinMax::~NetEBMinMax() @@ -262,30 +131,15 @@ ivl_variable_type_t NetEBMinMax::expr_type() const return IVL_VT_LOGIC; } -NetEBMult::NetEBMult(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBMult::NetEBMult(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - if (expr_type() == IVL_VT_REAL) { - expr_width(1); - cast_signed(true); - } else { - expr_width(l->expr_width() + r->expr_width()); - cast_signed(l->has_sign() && r->has_sign()); - } } NetEBMult::~NetEBMult() { } -NetEBMult* NetEBMult::dup_expr() const -{ - NetEBMult*result = new NetEBMult(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - ivl_variable_type_t NetEBMult::expr_type() const { if (left_->expr_type() == IVL_VT_REAL) @@ -297,27 +151,15 @@ ivl_variable_type_t NetEBMult::expr_type() const return IVL_VT_LOGIC; } -NetEBPow::NetEBPow(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBPow::NetEBPow(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - assert(op__ == 'p'); - /* You could need up to a * (2^b - 1) bits. */ - expr_width(l->expr_width()); - cast_signed(l->has_sign() || r->has_sign()); } NetEBPow::~NetEBPow() { } -NetEBPow* NetEBPow::dup_expr() const -{ - NetEBPow*result = new NetEBPow(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - ivl_variable_type_t NetEBPow::expr_type() const { if (right_->expr_type() == IVL_VT_REAL) @@ -328,13 +170,9 @@ ivl_variable_type_t NetEBPow::expr_type() const return IVL_VT_LOGIC; } -NetEBShift::NetEBShift(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBShift::NetEBShift(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - expr_width(l->expr_width()); - - // The >>> is signed if the left operand is signed. - if (op__ == 'R') cast_signed(l->has_sign()); } NetEBShift::~NetEBShift() @@ -346,24 +184,9 @@ bool NetEBShift::has_width() const return left_->has_width(); } -NetEBShift* NetEBShift::dup_expr() const -{ - NetEBShift*result = new NetEBShift(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - -NetEConcat::NetEConcat(unsigned cnt, NetExpr* r) +NetEConcat::NetEConcat(unsigned cnt, unsigned r) : parms_(cnt), repeat_(r) { - if (repeat_ == 0) { - repeat_calculated_ = true; - repeat_value_ = 1; - } else { - repeat_calculated_ = false; - } - expr_width(0); } @@ -383,63 +206,7 @@ void NetEConcat::set(unsigned idx, NetExpr*e) assert(idx < parms_.count()); assert(parms_[idx] == 0); parms_[idx] = e; - expr_width( expr_width() + e->expr_width() ); -} - -NetEConcat* NetEConcat::dup_expr() const -{ - NetEConcat*dup = new NetEConcat(parms_.count(), 0); - dup->set_line(*this); - for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) - if (parms_[idx]) { - NetExpr*tmp = parms_[idx]->dup_expr(); - assert(tmp); - dup->parms_[idx] = tmp; - } - - - dup->repeat_ = repeat_? repeat_->dup_expr() : 0; - dup->repeat_value_ = repeat_value_; - dup->repeat_calculated_ = repeat_calculated_; - dup->expr_width(expr_width()); - - return dup; -} - -unsigned NetEConcat::repeat() -{ - if (repeat_calculated_) - return repeat_value_; - - eval_expr(repeat_); - - NetEConst*repeat_const = dynamic_cast(repeat_); - - /* This should not be possible, as it was checked earlier to - assure that this is a constant expression. */ - if (repeat_const == 0) { - cerr << get_fileline() << ": internal error: repeat expression " - << "is not a compile time constant." << endl; - cerr << get_fileline() << ": : Expression is: " - << *repeat_ << endl; - repeat_calculated_ = true; - repeat_value_ = 1; - return 1; - } - - repeat_calculated_ = true; - repeat_value_ = repeat_const->value().as_ulong(); - - delete repeat_; - repeat_ = 0; - - return repeat_value_; -} - -unsigned NetEConcat::repeat() const -{ - assert(repeat_calculated_); - return repeat_value_; + expr_width( expr_width() + repeat_ * e->expr_width() ); } NetEConstEnum::NetEConstEnum(NetScope*s, perm_string n, netenum_t*eset, const verinum&v) @@ -461,7 +228,7 @@ NetECReal::NetECReal(const verireal&val) : value_(val) { expr_width(1); - cast_signed(true); + cast_signed_base_(true); } NetECReal::~NetECReal() @@ -475,14 +242,7 @@ const verireal& NetECReal::value() const bool NetECReal::has_width() const { - return false; -} - -NetECReal* NetECReal::dup_expr() const -{ - NetECReal*tmp = new NetECReal(value_); - tmp->set_line(*this); - return tmp; + return true; } ivl_variable_type_t NetECReal::expr_type() const diff --git a/net_proc.cc b/net_proc.cc index 103deeb7e..83487dd91 100644 --- a/net_proc.cc +++ b/net_proc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2011 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 @@ -100,8 +100,6 @@ void NetCase::set_case(unsigned idx, NetExpr*e, NetProc*p) assert(idx < nitems_); items_[idx].guard = e; items_[idx].statement = p; - if (items_[idx].guard) - items_[idx].guard->set_width(expr_->expr_width()); } NetDisable::NetDisable(NetScope*tgt) @@ -169,4 +167,3 @@ const NetExpr* NetRepeat::expr() const { return expr_; } - diff --git a/netlist.cc b/netlist.cc index 05e3899a0..6af72c71d 100644 --- a/netlist.cc +++ b/netlist.cc @@ -2095,21 +2095,9 @@ NetExpr::~NetExpr() { } -bool NetExpr::has_sign() const -{ - return signed_flag_; -} - void NetExpr::cast_signed(bool flag) { - signed_flag_ = flag; -} - -void NetExpr::expr_width(unsigned w) -{ - // Catch underflow wrap errors. - ivl_assert(*this, w < (UINT_MAX - 256)); - width_ = w; + cast_signed_base_(flag); } bool NetExpr::has_width() const @@ -2119,37 +2107,22 @@ bool NetExpr::has_width() const /* * Create a bitwise operator node from the opcode and the left and - * right expressions. Don't worry about the width of the expression - * yet, we'll get that from the l-value, whatever that turns out to - * be. However, if we don't, our default will be the width of the - * largest operand. + * right expressions. */ -NetEBBits::NetEBBits(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBBits::NetEBBits(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - if (r->expr_width() > l->expr_width()) - expr_width(r->expr_width()); - else - expr_width(l->expr_width()); } NetEBBits::~NetEBBits() { } -NetEBBits* NetEBBits::dup_expr() const -{ - NetEBBits*result = new NetEBBits(op_, left_->dup_expr(), - right_->dup_expr()); - return result; -} - -NetEBinary::NetEBinary(char op__, NetExpr*l, NetExpr*r) +NetEBinary::NetEBinary(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) : op_(op__), left_(l), right_(r) { - // Binary expressions of all sorts are signed if both the - // arguments are signed. - cast_signed_base_( left_->has_sign() && right_->has_sign()); + expr_width(wid); + cast_signed_base_(signed_flag); } NetEBinary::~NetEBinary() @@ -2163,29 +2136,15 @@ bool NetEBinary::has_width() const return left_->has_width() && right_->has_width(); } -NetEBinary* NetEBinary::dup_expr() const -{ - assert(0); - return 0; -} - NetEBLogic::NetEBLogic(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +: NetEBinary(op__, l, r, 1, false) { - expr_width(1); } NetEBLogic::~NetEBLogic() { } -NetEBLogic* NetEBLogic::dup_expr() const -{ - NetEBLogic*result = new NetEBLogic(op_, left_->dup_expr(), - right_->dup_expr()); - return result; -} - NetEConst::NetEConst(const verinum&val) : NetExpr(val.len()), value_(val) { @@ -2196,6 +2155,12 @@ NetEConst::~NetEConst() { } +void NetEConst::cast_signed(bool flag) +{ + cast_signed_base_(flag); + value_.has_sign(flag); +} + const verinum& NetEConst::value() const { return value_; @@ -2218,6 +2183,17 @@ ivl_variable_type_t NetEConst::expr_type() const return IVL_VT_LOGIC; } +void NetEConst::trim() + +{ + if (value_.is_string()) + return; + + value_.has_len(false); + value_ = trim_vnum(value_); + expr_width(value_.len()); +} + NetEConstParam::NetEConstParam(NetScope*s, perm_string n, const verinum&v) : NetEConst(v), scope_(s), name_(n) { @@ -2272,7 +2248,7 @@ NetESignal::NetESignal(NetNet*n) { net_->incr_eref(); set_line(*n); - cast_signed(net_->get_signed()); + cast_signed_base_(net_->get_signed()); } NetESignal::NetESignal(NetNet*n, NetExpr*w) @@ -2280,7 +2256,7 @@ NetESignal::NetESignal(NetNet*n, NetExpr*w) { net_->incr_eref(); set_line(*n); - cast_signed(net_->get_signed()); + cast_signed_base_(net_->get_signed()); } NetESignal::~NetESignal() @@ -2334,20 +2310,12 @@ ivl_variable_type_t NetESignal::expr_type() const * should have the same width. NOTE: This matching of the widths really * has to be done in elaboration. */ -NetETernary::NetETernary(NetExpr*c, NetExpr*t, NetExpr*f) +NetETernary::NetETernary(NetExpr*c, NetExpr*t, NetExpr*f, + unsigned wid, bool signed_flag) : cond_(c), true_val_(t), false_val_(f) { - if (type_is_vectorable(expr_type())) { - // use widest result - if (true_val_->expr_width() > false_val_->expr_width()) - expr_width(true_val_->expr_width()); - else - expr_width(false_val_->expr_width()); - } else { - expr_width(1); - } - - cast_signed(c->has_sign() && t->has_sign() && f->has_sign()); + expr_width(wid); + cast_signed_base_(signed_flag); } NetETernary::~NetETernary() @@ -2399,23 +2367,10 @@ ivl_variable_type_t NetETernary::expr_type() const return tru; } -NetEUnary::NetEUnary(char op__, NetExpr*ex) -: NetExpr(ex->expr_width()), op_(op__), expr_(ex) +NetEUnary::NetEUnary(char op__, NetExpr*ex, unsigned wid, bool signed_flag) +: NetExpr(wid), op_(op__), expr_(ex) { - switch (op_) { - case '!': - expr_width(1); - break; - } - switch (op_) { - case '-': - case '+': - case 'm': // abs() - cast_signed(ex->has_sign()); - break; - default: - ; - } + cast_signed_base_(signed_flag); } NetEUnary::~NetEUnary() @@ -2428,8 +2383,8 @@ ivl_variable_type_t NetEUnary::expr_type() const return expr_->expr_type(); } -NetEUBits::NetEUBits(char op__, NetExpr*ex) -: NetEUnary(op__, ex) +NetEUBits::NetEUBits(char op__, NetExpr*ex, unsigned wid, bool signed_flag) +: NetEUnary(op__, ex, wid, signed_flag) { } @@ -2443,9 +2398,8 @@ ivl_variable_type_t NetEUBits::expr_type() const } NetEUReduce::NetEUReduce(char op__, NetExpr*ex) -: NetEUnary(op__, ex) +: NetEUnary(op__, ex, 1, false) { - expr_width(1); } NetEUReduce::~NetEUReduce() @@ -2457,8 +2411,8 @@ ivl_variable_type_t NetEUReduce::expr_type() const return expr_->expr_type(); } -NetECast::NetECast(char op__, NetExpr*ex) -: NetEUnary(op__, ex) +NetECast::NetECast(char op__, NetExpr*ex, unsigned wid, bool signed_flag) +: NetEUnary(op__, ex, wid, signed_flag) { } diff --git a/netlist.h b/netlist.h index 676a348fd..623d023c0 100644 --- a/netlist.h +++ b/netlist.h @@ -1601,12 +1601,8 @@ class NetTran : public NetNode, public IslandBranch { * There are cases where expressions need to be represented. The * NetExpr class is the root of a hierarchy that serves that purpose. * - * The expr_width() is the width of the expression, that accounts - * for the widths of the sub-expressions I might have. It is up to the - * derived classes to properly set the expr width, if need be. The - * set_width() method is used to compel an expression to have a - * certain width, and is used particularly when the expression is an - * rvalue in an assignment statement. + * The expr_width() is the width of the expression, which is calculated + * before the expression is elaborated. */ class NetExpr : public LineInfo { public: @@ -1622,27 +1618,9 @@ class NetExpr : public LineInfo { // How wide am I? unsigned expr_width() const { return width_; } - // Coerce the expression to have a specific width. If the - // coercion works, then return true. Otherwise, return false. - // A coercion will work or not depending on the implementation - // in the derived class. Normally, the width will be set if - // the expression is: - // - already the requested size, OR - // - otherwise unsized. - // Normally, the resize will not allow a width size that loses - // data. For example, it will not reduce a constant expression - // to the point where significant bits are lost. But if the - // last_chance flag is true, then the method assumes that high - // bits will be lost anyhow, so try harder. Loss will be - // allowed, but it still won't resize fixed size expressions - // such as vector signals. This flag is meant to be used by - // elaboration of procedural assignment to set the expression - // width to the l-value width, if possible. - virtual bool set_width(unsigned wid, bool last_chance =false); - // This method returns true if the expression is // signed. Unsigned expressions return false. - bool has_sign() const; + bool has_sign() const { return signed_flag_; } virtual void cast_signed(bool flag); // This returns true if the expression has a definite @@ -1664,14 +1642,7 @@ class NetExpr : public LineInfo { // equivalent expression that is reduced as far as compile // time knows how. Essentially, this is designed to fold // constants. - // - // The prune_to_width is the maximum width that the result - // should be. If it is 0 or -1, then do not prune the - // result. If it is -1, go through special efforts to preserve - // values that may expand. A width of 0 corresponds to a - // self-determined context, and a width of -1 corresponds to - // an infinitely wide context. - virtual NetExpr*eval_tree(int prune_to_width = -1); + virtual NetExpr*eval_tree(); // Make a duplicate of myself, and subexpressions if I have // any. This is a deep copy operation. @@ -1696,10 +1667,9 @@ class NetExpr : public LineInfo { // the expression output. virtual NetNet*synthesize(Design*des, NetScope*scope, NetExpr*root); - protected: - void expr_width(unsigned w); - void cast_signed_base_(bool flag) {signed_flag_ = flag; } + void expr_width(unsigned wid) { width_ = wid; } + void cast_signed_base_(bool flag) { signed_flag_ = flag; } private: unsigned width_; @@ -1723,11 +1693,15 @@ class NetEConst : public NetExpr { const verinum&value() const; - virtual bool set_width(unsigned w, bool last_chance =false); - virtual void cast_signed(bool sign_flag); + virtual void cast_signed(bool flag); virtual bool has_width() const; virtual ivl_variable_type_t expr_type() const; + /* This method allows the constant value to be converted + to an unsized value. This is used after evaluating a + unsized constant expression. */ + virtual void trim(); + virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -1750,7 +1724,6 @@ class NetEConstEnum : public NetEConst { const NetScope*scope() const; netenum_t*enumeration() const; - virtual bool set_width(unsigned w, bool last_chance =false); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -1772,7 +1745,6 @@ class NetEConstParam : public NetEConst { perm_string name() const; const NetScope*scope() const; - virtual bool set_width(unsigned w, bool last_chance =false); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -1794,10 +1766,6 @@ class NetECReal : public NetExpr { const verireal&value() const; - // Reals can be used in vector expressions. Conversions will - // be done at the right time. - virtual bool set_width(unsigned w, bool last_chance); - // This type has no self-determined width. This is false. virtual bool has_width() const; @@ -3129,14 +3097,13 @@ class NetEUFunc : public NetExpr { const NetScope* func() const; - virtual bool set_width(unsigned, bool last_chance); virtual ivl_variable_type_t expr_type() const; virtual void dump(ostream&) const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEUFunc*dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root); private: @@ -3332,7 +3299,7 @@ class NetAnalogTop : public LineInfo, public Attrib { class NetEBinary : public NetExpr { public: - NetEBinary(char op, NetExpr*l, NetExpr*r); + NetEBinary(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBinary(); const NetExpr*left() const { return left_; } @@ -3340,8 +3307,6 @@ class NetEBinary : public NetExpr { char op() const { return op_; } - virtual bool set_width(unsigned w, bool last_chance =false); - // A binary expression node only has a definite // self-determinable width if the operands both have definite // widths. @@ -3371,15 +3336,13 @@ class NetEBinary : public NetExpr { class NetEBAdd : public NetEBinary { public: - NetEBAdd(char op, NetExpr*l, NetExpr*r, bool lossless_flag =false); + NetEBAdd(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBAdd(); virtual ivl_variable_type_t expr_type() const; - virtual bool set_width(unsigned w, bool last_chance); - virtual void cast_signed(bool sign_flag); virtual NetEBAdd* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3394,15 +3357,13 @@ class NetEBAdd : public NetEBinary { class NetEBDiv : public NetEBinary { public: - NetEBDiv(char op, NetExpr*l, NetExpr*r); + NetEBDiv(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBDiv(); virtual ivl_variable_type_t expr_type() const; - virtual bool set_width(unsigned w, bool last_chance); - virtual void cast_signed(bool sign_flag); virtual NetEBDiv* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3426,12 +3387,11 @@ class NetEBDiv : public NetEBinary { class NetEBBits : public NetEBinary { public: - NetEBBits(char op, NetExpr*l, NetExpr*r); + NetEBBits(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBBits(); - virtual bool set_width(unsigned w, bool last_chance); virtual NetEBBits* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); }; @@ -3456,13 +3416,11 @@ class NetEBComp : public NetEBinary { NetEBComp(char op, NetExpr*l, NetExpr*r); ~NetEBComp(); - virtual bool set_width(unsigned w, bool last_chance =false); - /* A compare expression has a definite width. */ virtual bool has_width() const; virtual ivl_variable_type_t expr_type() const; virtual NetEBComp* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); @@ -3492,9 +3450,8 @@ class NetEBLogic : public NetEBinary { NetEBLogic(char op, NetExpr*l, NetExpr*r); ~NetEBLogic(); - virtual bool set_width(unsigned w, bool last_chance =false); virtual NetEBLogic* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3511,7 +3468,7 @@ class NetEBLogic : public NetEBinary { class NetEBMinMax : public NetEBinary { public: - NetEBMinMax(char op, NetExpr*l, NetExpr*r); + NetEBMinMax(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBMinMax(); virtual ivl_variable_type_t expr_type() const; @@ -3525,15 +3482,13 @@ class NetEBMinMax : public NetEBinary { class NetEBMult : public NetEBinary { public: - NetEBMult(char op, NetExpr*l, NetExpr*r); + NetEBMult(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBMult(); virtual ivl_variable_type_t expr_type() const; - virtual bool set_width(unsigned w, bool last_chance); - virtual void cast_signed(bool sign_flag); virtual NetEBMult* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3546,14 +3501,13 @@ class NetEBMult : public NetEBinary { class NetEBPow : public NetEBinary { public: - NetEBPow(char op, NetExpr*l, NetExpr*r); + NetEBPow(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBPow(); virtual ivl_variable_type_t expr_type() const; - virtual bool set_width(unsigned w, bool last_chance); virtual NetEBPow* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3572,17 +3526,15 @@ class NetEBPow : public NetEBinary { class NetEBShift : public NetEBinary { public: - NetEBShift(char op, NetExpr*l, NetExpr*r); + NetEBShift(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBShift(); - virtual bool set_width(unsigned w, bool last_chance); - // A shift expression only needs the left expression to have a // definite width to give the expression a definite width. virtual bool has_width() const; virtual NetEBShift* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); @@ -3602,31 +3554,27 @@ class NetEBShift : public NetEBinary { class NetEConcat : public NetExpr { public: - NetEConcat(unsigned cnt, NetExpr* repeat =0); + NetEConcat(unsigned cnt, unsigned repeat =1); ~NetEConcat(); // Manipulate the parameters. void set(unsigned idx, NetExpr*e); - unsigned repeat(); - unsigned repeat() const; + unsigned repeat() const { return repeat_; } unsigned nparms() const { return parms_.count() ; } NetExpr* parm(unsigned idx) const { return parms_[idx]; } virtual NexusSet* nex_input(bool rem_out = true); virtual bool has_width() const; - virtual bool set_width(unsigned w, bool last_chance =false); virtual NetEConcat* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; private: svectorparms_; - NetExpr* repeat_; - unsigned repeat_value_; - bool repeat_calculated_; + unsigned repeat_; }; @@ -3656,10 +3604,9 @@ class NetESelect : public NetExpr { ivl_select_type_t select_type() const; virtual NexusSet* nex_input(bool rem_out = true); - virtual bool set_width(unsigned w, bool last_chance =false); virtual bool has_width() const; virtual void expr_scan(struct expr_scan_t*) const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetESelect* dup_expr() const; virtual NetNet*synthesize(Design*des, NetScope*scope, NetExpr*root); virtual void dump(ostream&) const; @@ -3757,11 +3704,10 @@ class NetESFunc : public NetExpr { NetExpr* parm(unsigned idx); const NetExpr* parm(unsigned idx) const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); - virtual bool set_width(unsigned, bool last_chance); virtual netenum_t* enumeration() const; virtual void dump(ostream&) const; @@ -3788,17 +3734,15 @@ class NetESFunc : public NetExpr { class NetETernary : public NetExpr { public: - NetETernary(NetExpr*c, NetExpr*t, NetExpr*f); + NetETernary(NetExpr*c, NetExpr*t, NetExpr*f, unsigned wid, bool signed_flag); ~NetETernary(); - virtual bool set_width(unsigned w, bool last_chance); - const NetExpr*cond_expr() const; const NetExpr*true_expr() const; const NetExpr*false_expr() const; virtual NetETernary* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); @@ -3837,16 +3781,14 @@ class NetETernary : public NetExpr { class NetEUnary : public NetExpr { public: - NetEUnary(char op, NetExpr*ex); + NetEUnary(char op, NetExpr*ex, unsigned wid, bool signed_flag); ~NetEUnary(); char op() const { return op_; } const NetExpr* expr() const { return expr_; } - virtual bool set_width(unsigned w, bool last_chance); - virtual NetEUnary* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual ivl_variable_type_t expr_type() const; @@ -3865,13 +3807,13 @@ class NetEUnary : public NetExpr { class NetEUBits : public NetEUnary { public: - NetEUBits(char op, NetExpr*ex); + NetEUBits(char op, NetExpr*ex, unsigned wid, bool signed_flag); ~NetEUBits(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual NetEUBits* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual ivl_variable_type_t expr_type() const; }; @@ -3881,10 +3823,9 @@ class NetEUReduce : public NetEUnary { NetEUReduce(char op, NetExpr*ex); ~NetEUReduce(); - virtual bool set_width(unsigned w, bool last_chance); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual NetEUReduce* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual ivl_variable_type_t expr_type() const; private: @@ -3894,7 +3835,7 @@ class NetEUReduce : public NetEUnary { class NetECast : public NetEUnary { public: - NetECast(char op, NetExpr*ex); + NetECast(char op, NetExpr*ex, unsigned wid, bool signed_flag); ~NetECast(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); @@ -3920,7 +3861,6 @@ class NetESignal : public NetExpr { ~NetESignal(); perm_string name() const; - virtual bool set_width(unsigned, bool last_chance); virtual NetESignal* dup_expr() const; NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root); diff --git a/netmisc.cc b/netmisc.cc index 4d0ea5c06..d3fc7a18d 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -184,9 +184,9 @@ NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src) NetExpr* cast_to_int2(NetExpr*expr) { - NetECast*cast = new NetECast('2', expr); + NetECast*cast = new NetECast('2', expr, expr->expr_width(), + expr->has_sign()); cast->set_line(*expr); - cast->cast_signed(expr->has_sign()); return cast; } @@ -208,17 +208,14 @@ static NetExpr* make_add_expr(NetExpr*expr, long val) val = -val; } - verinum val_v (val); + verinum val_v (val, expr->expr_width()); val_v.has_sign(true); - if (expr->has_width()) { - val_v = verinum(val_v, expr->expr_width()); - } - NetEConst*val_c = new NetEConst(val_v); val_c->set_line(*expr); - NetEBAdd*res = new NetEBAdd(add_op, expr, val_c); + NetEBAdd*res = new NetEBAdd(add_op, expr, val_c, expr->expr_width(), + expr->has_sign()); res->set_line(*expr); return res; @@ -235,7 +232,8 @@ static NetExpr* make_sub_expr(long val, NetExpr*expr) NetEConst*val_c = new NetEConst(val_v); val_c->set_line(*expr); - NetEBAdd*res = new NetEBAdd('-', val_c, expr); + NetEBAdd*res = new NetEBAdd('-', val_c, expr, expr->expr_width(), + expr->has_sign()); res->set_line(*expr); return res; @@ -444,11 +442,10 @@ NetExpr* condition_reduce(NetExpr*expr) return expr; verinum zero (verinum::V0, expr->expr_width()); + zero.has_sign(expr->has_sign()); NetEConst*ezero = new NetEConst(zero); ezero->set_line(*expr); - ezero->cast_signed(expr->has_sign()); - ezero->set_width(expr->expr_width()); NetEBComp*cmp = new NetEBComp('n', expr, ezero); cmp->set_line(*expr); @@ -457,43 +454,130 @@ NetExpr* condition_reduce(NetExpr*expr) return cmp; } -void probe_expr_width(Design*des, NetScope*scope, PExpr*pe) +static const char*width_mode_name(PExpr::width_mode_t mode) { - ivl_variable_type_t expr_type = IVL_VT_NO_TYPE; - bool flag = false; - pe->test_width(des, scope, 0, 0, expr_type, flag); + switch (mode) { + case PExpr::SIZED: + return "sized"; + case PExpr::LOSSLESS: + return "lossless"; + case PExpr::UNSIZED: + return "unsized"; + default: + return "??"; + } } -NetExpr* elab_and_eval(Design*des, NetScope*scope, - const PExpr*pe, int expr_wid, int prune_width) +NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, int context_width) { - NetExpr*tmp = pe->elaborate_expr(des, scope, expr_wid, false); + PExpr::width_mode_t mode = PExpr::SIZED; + if ((context_width == -2) && !gn_strict_expr_width_flag) + mode = PExpr::EXPAND; + + pe->test_width(des, scope, mode); + + // Get the final expression width. If the expression is unsized, + // this may be different from the value returned by test_width(). + unsigned expr_width = pe->expr_width(); + + // If context_width is positive, this is the RHS of an assignment, + // so the LHS width must also be included in the width calculation. + if ((context_width > 0) && (pe->expr_type() != IVL_VT_REAL) + && (expr_width < (unsigned)context_width)) + expr_width = context_width; + + if (debug_elaborate) { + cerr << pe->get_fileline() << ": debug: test_width of " + << *pe << endl; + cerr << pe->get_fileline() << ": " + << "returns type=" << pe->expr_type() + << ", width=" << expr_width + << ", signed=" << pe->has_sign() + << ", mode=" << width_mode_name(mode) << endl; + } + + // If we can get the same result using a smaller expression + // width, do so. + if ((context_width > 0) && (pe->expr_type() != IVL_VT_REAL) + && (expr_width > (unsigned)context_width)) { + expr_width = max(pe->min_width(), (unsigned)context_width); + + if (debug_elaborate) { + cerr << pe->get_fileline() << ": " + << "pruned to width=" << expr_width << endl; + } + } + + NetExpr*tmp = pe->elaborate_expr(des, scope, expr_width, false); if (tmp == 0) return 0; - eval_expr(tmp, prune_width); + eval_expr(tmp, context_width); + + if (NetEConst*ce = dynamic_cast(tmp)) { + if ((mode >= PExpr::LOSSLESS) && (context_width < 0)) + ce->trim(); + } return tmp; } -void eval_expr(NetExpr*&expr, int prune_width) +NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, + unsigned arg_idx, PExpr*pe) +{ + PExpr::width_mode_t mode = PExpr::SIZED; + pe->test_width(des, scope, mode); + + if (debug_elaborate) { + cerr << pe->get_fileline() << ": debug: test_width of " + << name << " argument " << (arg_idx+1) << " " << *pe << endl; + cerr << pe->get_fileline() << ": " + << "returns type=" << pe->expr_type() + << ", width=" << pe->expr_width() + << ", signed=" << pe->has_sign() + << ", mode=" << width_mode_name(mode) << endl; + } + + NetExpr*tmp = pe->elaborate_expr(des, scope, pe->expr_width(), true); + if (tmp == 0) return 0; + + eval_expr(tmp, -1); + + if (NetEConst*ce = dynamic_cast(tmp)) { + if (mode != PExpr::SIZED) + ce->trim(); + } + + return tmp; +} + +void eval_expr(NetExpr*&expr, int context_width) { assert(expr); if (dynamic_cast(expr)) return; - /* Resize a constant if allowed and needed. */ - if (NetEConst *tmp = dynamic_cast(expr)) { - if (prune_width <= 0) return; - if (tmp->has_width()) return; - if ((unsigned)prune_width <= tmp->expr_width()) return; - expr = pad_to_width(expr, (unsigned)prune_width, *expr); - return; - } - NetExpr*tmp = expr->eval_tree(prune_width); + NetExpr*tmp = expr->eval_tree(); if (tmp != 0) { tmp->set_line(*expr); delete expr; expr = tmp; } + + if (context_width <= 0) return; + + NetEConst *ce = dynamic_cast(expr); + if (ce == 0) return; + + // The expression is a constant, so resize it if needed. + if (ce->expr_width() < (unsigned)context_width) { + expr = pad_to_width(expr, context_width, *expr); + } + if (ce->expr_width() > (unsigned)context_width) { + verinum value(ce->value(), context_width); + ce = new NetEConst(value); + ce->set_line(*expr); + delete expr; + expr = ce; + } } bool eval_as_long(long&value, NetExpr*expr) @@ -560,7 +644,6 @@ hname_t eval_path_component(Design*des, NetScope*scope, assert(index.sel == index_component_t::SEL_BIT); // Evaluate the bit select to get a number. - probe_expr_width(des, scope, index.msb); NetExpr*tmp = elab_and_eval(des, scope, index.msb, -1); ivl_assert(*index.msb, tmp); diff --git a/netmisc.h b/netmisc.h index 14c6628df..6f98d30d4 100644 --- a/netmisc.h +++ b/netmisc.h @@ -1,7 +1,7 @@ #ifndef __netmisc_H #define __netmisc_H /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2011 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 @@ -151,45 +151,38 @@ extern bool is_param_expr; * constant expression. If it cannot be evaluated, it returns whatever * it can. If the expression cannot be elaborated, return 0. * - * The expr_width is the width of the context where the expression is - * being elaborated, or -1 if the expression is self-determined width. - * - * The prune_width is the maximum width of the result, and is passed - * to the eval_tree method of the expression to limit constant - * results. The evaluation will prune any constant result down to the - * prune_width (if >0) so should only be used at the point where it is - * bound to the destination. + * The context_width is the width of the context where the expression is + * being elaborated, or -1 if the expression is self-determined, or -2 + * if the expression is lossless self-determined (this last option is + * treated as standard self-determined if the gn_strict_expr_width flag + * is set). */ class PExpr; extern NetExpr* elab_and_eval(Design*des, NetScope*scope, - const PExpr*pe, int expr_wid, - int prune_width =-1); - -void probe_expr_width(Design*des, NetScope*scope, PExpr*pe); + PExpr*pe, int context_width); +/* + * This function is a variant of elab_and_eval that elaborates and + * evaluates the arguments of a system task. + */ +extern NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, + unsigned arg_idx, PExpr*pe); /* * This function elaborates an expression as if it is for the r-value - * of an assignment, The data_type_lv and expr_wid_lv are the type and - * with of the l-value, and the expr is the expression to - * elaborate. The result is the NetExpr elaborated and evaluated. - * (See elab_expr.cc) + * of an assignment, The lv_type and lv_width are the type and width + * of the l-value, and the expr is the expression to elaborate. The + * result is the NetExpr elaborated and evaluated. (See elab_expr.cc) */ extern NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, - ivl_variable_type_t data_type_lv, - int expr_wid_lv, PExpr*expr); + ivl_variable_type_t lv_type, + unsigned lv_width, PExpr*expr); /* - * Used by elaboration to suppress the sign of an operand if the other - * is unsigned. - */ -extern void suppress_binary_operand_sign_if_needed(NetExpr*lp, NetExpr*rp); - -/* - * This procedure elaborates an expression and if the elaboration is + * This procedure evaluates an expression and if the evaluation is * successful the original expression is replaced with the new one. */ -void eval_expr(NetExpr*&expr, int prune_width =-1); +void eval_expr(NetExpr*&expr, int context_width =-1); /* * Get the long integer value for the passed in expression, if diff --git a/set_width.cc b/set_width.cc deleted file mode 100644 index 9e5dd4278..000000000 --- a/set_width.cc +++ /dev/null @@ -1,486 +0,0 @@ -/* - * Copyright (c) 1999-2010 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 - -/* - * This file contains set_width methods for the various NetExpr - * classes. The set_width method is used by elaboration to ask the - * expression to resize itself. If the expression can't, then the - * set_width method will return false and the caller will arrange for - * whatever is needed to deal with the size mismatch. - */ -# include "netlist.h" -# include "netmisc.h" -# include "compiler.h" -# include - - -bool NetExpr::set_width(unsigned w, bool) -{ - cerr << get_fileline() << ": internal warning: " - <>) - /* these operators are handled in the derived class. */ - assert(0); - break; - - /* The default rule is that the operands of the binary - operator might as well use the same width as the - output from the binary operation. */ - default: - expr_width(left_->expr_width() > right_->expr_width() - ? left_->expr_width() : right_->expr_width()); - cerr << "NetEBinary::set_width(): Using default for " << - op_ << "." << endl; - flag = false; - - case '%': - case '/': - flag = left_->set_width(w) && flag; - flag = right_->set_width(w) && flag; - expr_width(w); - break; - } - return flag; -} - -/* - * The bitwise logical operators have operands the same size as the - * result. Anything else is a mess. - */ -bool NetEBAdd::set_width(unsigned w, bool) -{ - - unsigned wid = w; - if (left_->expr_width() > wid) - wid = left_->expr_width(); - if (right_->expr_width() > wid) - wid = right_->expr_width(); - - left_->set_width(wid); - right_->set_width(wid); - - if (left_->expr_width() < wid) { - NetExpr*tmp = new NetESelect(left_, 0, wid); - tmp->cast_signed(left_->has_sign()); - left_ = tmp; - } - - if (right_->expr_width() < wid) { - NetExpr*tmp = new NetESelect(right_, 0, wid); - tmp->cast_signed(right_->has_sign()); - right_ = tmp; - } - - expr_width(wid); - return w == wid; -} - -void NetEBAdd::cast_signed(bool sign_flag) -{ - if (has_sign() == sign_flag) - return; - - if (sign_flag == false) { - left_->cast_signed(sign_flag); - right_->cast_signed(sign_flag); - } - cast_signed_base_(sign_flag); -} - -/* - * The bitwise logical operators have operands the same size as the - * result. Anything else is a mess. I first try to get the operands to - * shrink to the desired size. I then expand operands that are too small. - */ -bool NetEBBits::set_width(unsigned w, bool) -{ - /* First, give the operands a chance to adjust themselves to - the requested width. */ - left_->set_width(w); - right_->set_width(w); - - - /* */ - - unsigned use_width = w; - if (left_->expr_width() > use_width) - use_width = left_->expr_width(); - if (right_->expr_width() > use_width) - use_width = right_->expr_width(); - - /* If the operands end up too small, then pad them to suit. */ - - if (left_->expr_width() < use_width) { - NetExpr*tmp = pad_to_width(left_, use_width, *this); - assert(tmp); - left_ = tmp; - } - - if (right_->expr_width() < w) { - NetExpr*tmp = pad_to_width(right_, use_width, *this); - assert(tmp); - right_ = tmp; - } - - - /* And here is the final width. If this is not the size the - caller requested, then return false. Otherwise, return - true. */ - expr_width(use_width); - return w == use_width; -} - -/* - * Comparison operators allow the subexpressions to have - * their own natural width, but the comparison operator result has a - * fixed width of 1. - */ -bool NetEBComp::set_width(unsigned w, bool) -{ - return w == 1; -} - -/* - * There is nothing we can do to the operands of a division to make it - * confirm to the requested width. Force the context to pad or truncate. - */ -bool NetEBDiv::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -void NetEBDiv::cast_signed(bool sign_flag) -{ - if (has_sign() == sign_flag) - return; - - if (sign_flag == false) { - left_->cast_signed(sign_flag); - right_->cast_signed(sign_flag); - } - - cast_signed_base_(sign_flag); -} - -bool NetEBLogic::set_width(unsigned w, bool) -{ - bool flag; - flag = left_->set_width(right_->expr_width()); - if (!flag) - flag = right_->set_width(left_->expr_width()); - return w == 1; -} - -/* - * There is nothing we can do to the operands of a multiply to make it - * confirm to the requested width. Force the context to pad or truncate. - */ -bool NetEBMult::set_width(unsigned w, bool) -{ - if (w < left_->expr_width()) - left_->set_width(w); - if (w < right_->expr_width()) - right_->expr_width(); - - expr_width(w); - return true; -} - -void NetEBMult::cast_signed(bool sign_flag) -{ - if (has_sign() == sign_flag) - return; - - if (sign_flag == false) { - left_->cast_signed(sign_flag); - right_->cast_signed(sign_flag); - } - - cast_signed_base_(sign_flag); -} - -bool NetEBPow::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -/* - * The shift operator allows the shift amount to have its own - * natural width. The width of the operator result is the width of the - * left operand, the value that is to be shifted. - */ -bool NetEBShift::set_width(unsigned w, bool) -{ - switch (op()) { - - case 'l': - left_->set_width(w); - if (left_->expr_width() < w) - left_ = pad_to_width(left_, w, *this); - break; - - case 'r': - case 'R': - if (left_->expr_width() < w) - left_ = pad_to_width(left_, w, *this); - break; - - default: - assert(0); - } - - expr_width(left_->expr_width()); - - return w == expr_width(); -} - -/* - * Add up the widths from all the expressions that are concatenated - * together. This is the width of the expression, tough luck if you - * want it otherwise. - * - * If during the course of elaboration one of the sub-expressions is - * broken, then don't count it in the width. This doesn't really - * matter because the null expression is indication of an error and - * the compiler will not go beyond elaboration. - */ -bool NetEConcat::set_width(unsigned w, bool) -{ - unsigned sum = 0; - for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) - if (parms_[idx] != 0) - sum += parms_[idx]->expr_width(); - - sum *= repeat(); - expr_width(sum); - return w == sum; -} - -bool NetEConst::set_width(unsigned w, bool last_chance) -{ - /* Make the value signed if the NetEConst is signed. - * This happens when $signed() is called, so this - * sign information needs to be propagated. */ - value_.has_sign(has_sign()); - - if (w == value_.len()) { - expr_width(w); - return true; - } - - assert(w != 0); - - if (w > value_.len()) { - verinum::V pad = verinum::V0; - if (value_.has_sign()) { - pad = value_.get(value_.len()-1); - - /* It appears that you always have a defined length here, - * so this logic may be in error. */ - } else if (value_.len() != 0 && !value_.has_len()) - switch (value_.get(value_.len()-1)) { - case verinum::V1: - case verinum::V0: - break; - case verinum::Vx: - pad = verinum::Vx; - break; - case verinum::Vz: - pad = verinum::Vz; - break; - } - - verinum tmp (verinum::V0, w, has_width()); - for (unsigned idx = 0 ; idx < value_.len() ; idx += 1) - tmp.set(idx, value_[idx]); - for (unsigned idx = value_.len() ; idx < w ; idx += 1) - tmp.set(idx, pad); - - tmp.has_sign(value_.has_sign()); - value_ = tmp; - - expr_width(w); - return true; - - } else { - unsigned use_w = w; - - verinum::V pad_bit = value_.has_sign() - ? value_[value_.len() - 1] - : verinum::V0; - - if (! last_chance) { - // Don't reduce a number too small to hold all the - // significant bits. - for (unsigned idx = w ; idx < value_.len() ; idx += 1) - if (value_[idx] != pad_bit) - use_w = idx+1; - - // Correct for the special case of signed value. We - // cannot have the result change sign on us. - if (value_.has_sign() && (use_w < value_.len()) - && (value_[use_w-1] != pad_bit)) - use_w += 1; - } - - verinum tmp (verinum::V0, use_w, has_width()); - for (unsigned idx = 0 ; idx < use_w ; idx += 1) - tmp.set(idx, value_[idx]); - - tmp.has_sign(value_.has_sign()); - value_ = tmp; - expr_width(use_w); - return w == use_w; - } -} - -void NetEConst::cast_signed(bool sign_flag) -{ - value_.has_sign(sign_flag); - cast_signed_base_(sign_flag); -} - -/* - * Parameter vectors cannot be resized because they refer to a common - * value. Ditto for enumeration names. - */ -bool NetEConstParam::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -bool NetEConstEnum::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -/* - * Real constants can have whatever width the environment wants, - * because it isn't really a vector. The environment will convert this - * to a vector at the right time. - */ -bool NetECReal::set_width(unsigned w, bool) -{ - expr_width(w); - return true; -} -#if 0 -bool NetEMemory::set_width(unsigned w, bool) -{ - if (w != mem_->width()) - return false; - - expr_width(w); - return true; -} -#endif -bool NetESelect::set_width(unsigned, bool) -{ - return expr_width() == 1; -} - -bool NetESFunc::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -/* - * The signal should automatically pad with zeros to get to the desired - * width. Do not allow signal bits to be truncated, however. - */ -bool NetESignal::set_width(unsigned w, bool) -{ - return w == vector_width(); -} - -bool NetETernary::set_width(unsigned w, bool last_chance) -{ - bool flag = true; - flag = flag && true_val_->set_width(w, last_chance); - flag = flag && false_val_->set_width(w, last_chance); - - /* The ternary really insists that the true and false clauses - have the same width. Even if we fail to make the width that - the user requests, at least pad the smaller width to suit - the larger. */ - if (true_val_->expr_width() < false_val_->expr_width()) - true_val_ = pad_to_width(true_val_, false_val_->expr_width(), - *this); - if (false_val_->expr_width() < true_val_->expr_width()) - false_val_ = pad_to_width(false_val_, true_val_->expr_width(), - *this); - - expr_width(true_val_->expr_width()); - return flag; -} - -/* - * XXXX FIX ME: For now, just take whatever the caller says as my - * width. What I really need to do is note the width of the output - * parameter of the function definition and take that into account. - */ -bool NetEUFunc::set_width(unsigned w, bool) -{ - return w == result_sig_->expr_width(); -} - -bool NetEUnary::set_width(unsigned w, bool) -{ - bool flag = true; - switch (op_) { - case '~': - case '-': - case 'r': - case 'i': - flag = expr_->set_width(w); - expr_width(w); - break; - case '!': - return w == 1; - default: - flag = expr_width() == w; - break; - } - - return flag; -} - -/* - * Unary reduction operators allow its operand to have any width. The - * result is defined to be 1. - */ -bool NetEUReduce::set_width(unsigned w, bool) -{ - return w == 1; -} diff --git a/t-dll-expr.cc b/t-dll-expr.cc index 99fba7a13..0db2ffd62 100644 --- a/t-dll-expr.cc +++ b/t-dll-expr.cc @@ -274,8 +274,8 @@ void dll_target::expr_param(const NetEConstParam*net) << ivl_scope_name(scop) << endl; } assert(par); - assert(par->value); - expr_ = par->value; + expr_const(net); + expr_->u_.string_.parameter = par; } void dll_target::expr_rparam(const NetECRealParam*net) diff --git a/verinum.cc b/verinum.cc index 5c79077dc..1b576c224 100644 --- a/verinum.cc +++ b/verinum.cc @@ -287,7 +287,7 @@ verinum::verinum(const verinum&that) verinum::verinum(const verinum&that, unsigned nbits) { - string_flag_ = false; + string_flag_ = that.string_flag_ && (that.nbits_ == nbits); nbits_ = nbits; bits_ = new V[nbits_]; has_len_ = true; @@ -591,6 +591,39 @@ verinum pad_to_width(const verinum&that, unsigned width) return val; } +verinum cast_to_width(const verinum&that, unsigned width) +{ + if (that.has_len() && (that.len() == width)) + return that; + + if (that.len() >= width) + return verinum(that, width); + + if (that.len() == 0) { + verinum val (verinum::V0, width, true); + val.has_sign(that.has_sign()); + return val; + } + + verinum::V pad = that[that.len()-1]; + if (pad==verinum::V1 && !that.has_sign()) + pad = verinum::V0; + if (that.has_len() && !that.has_sign()) { + if (pad==verinum::Vx) + pad = verinum::V0; + if (pad==verinum::Vz) + pad = verinum::V0; + } + + verinum val(pad, width, true); + + for (unsigned idx = 0 ; idx < that.len() ; idx += 1) + val.set(idx, that[idx]); + + val.has_sign(that.has_sign()); + return val; +} + /* * This function returns a version of the verinum that has only as * many bits as are needed to accurately represent the value. It takes @@ -1097,6 +1130,7 @@ verinum pow(const verinum&left, const verinum&right) verinum operator << (const verinum&that, unsigned shift) { verinum result(verinum::V0, that.len() + shift, that.has_len()); + result.has_sign(that.has_sign()); for (unsigned idx = 0 ; idx < that.len() ; idx += 1) result.set(idx+shift, that.get(idx)); @@ -1119,6 +1153,7 @@ verinum operator >> (const verinum&that, unsigned shift) verinum result(that.has_sign()? that.get(that.len()-1) : verinum::V0, that.len() - shift, that.has_len()); + result.has_sign(that.has_sign()); for (unsigned idx = shift ; idx < that.len() ; idx += 1) result.set(idx-shift, that.get(idx)); diff --git a/verinum.h b/verinum.h index d44941291..acab1d673 100644 --- a/verinum.h +++ b/verinum.h @@ -1,7 +1,7 @@ #ifndef __verinum_H #define __verinum_H /* - * Copyright (c) 1998-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2011 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 @@ -64,6 +64,7 @@ class verinum { // A number "has a length" if the length was specified fixed // in some way. + bool has_len(bool flag) { has_len_ = flag; return has_len_; } bool has_len() const { return has_len_; } bool has_sign(bool flag) { has_sign_ = flag; return has_sign_; } @@ -126,6 +127,12 @@ inline verinum::V sign_bit(const verinum&val) extension, if the value is signed. */ extern verinum pad_to_width(const verinum&, unsigned width); +/* Return a verinum that has the same value as the input, but is + exactly the requested width. This may involve sign extension, + if the value is signed. The returned verinum will have fixed + width. */ +extern verinum cast_to_width(const verinum&, unsigned width); + /* Return a verinum that is minimal. That is, it has only the length needed to accurately represent the contained value, signed or not. */ extern verinum trim_vnum(const verinum&);