diff --git a/PExpr.cc b/PExpr.cc index 675eb1990..215396d01 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -156,6 +156,12 @@ PECastType::~PECastType() { } +PECastSign::PECastSign(bool signed_flag, PExpr *base) +: base_(base) +{ + signed_flag_ = signed_flag; +} + PEBComp::PEBComp(char op, PExpr*l, PExpr*r) : PEBinary(op, l, r) { diff --git a/PExpr.h b/PExpr.h index a57a69bb4..dc1b007a3 100644 --- a/PExpr.h +++ b/PExpr.h @@ -23,6 +23,7 @@ # include # include # include +# include # include "netlist.h" # include "verinum.h" # include "LineInfo.h" @@ -1012,6 +1013,26 @@ class PECastType : public PExpr { PExpr* base_; }; +/* + * Support the SystemVerilog sign cast. + */ +class PECastSign : public PExpr { + + public: + explicit PECastSign(bool signed_flag, PExpr *base); + ~PECastSign() = default; + + void dump(std::ostream &out) const; + + NetExpr* elaborate_expr(Design *des, NetScope *scope, + unsigned expr_wid, unsigned flags) const; + + unsigned test_width(Design *des, NetScope *scope, width_mode_t &mode); + + private: + std::unique_ptr base_; +}; + /* * This class is used for error recovery. All methods do nothing and return * null or default values. diff --git a/elab_expr.cc b/elab_expr.cc index b6a378d09..a3fe5431e 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1006,6 +1006,26 @@ NetExpr*PEBPower::elaborate_expr_leaf(Design*, NetExpr*lp, NetExpr*rp, return tmp; } +static unsigned int sign_cast_width(Design*des, NetScope*scope, PExpr &expr, + PExpr::width_mode_t&mode) +{ + unsigned int width; + + // The argument type/width is self-determined, but affects + // the result width. + PExpr::width_mode_t arg_mode = PExpr::SIZED; + width = expr.test_width(des, scope, arg_mode); + + if ((arg_mode >= PExpr::EXPAND) && type_is_vectorable(expr.expr_type())) { + if (mode < PExpr::LOSSLESS) + mode = PExpr::LOSSLESS; + if (width < integer_width) + width = integer_width; + } + + return width; +} + NetExpr*PEBShift::elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, unsigned expr_wid) const { @@ -1193,21 +1213,11 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, if (expr == 0) return 0; - // 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_width_ = sign_cast_width(des, scope, *expr, mode); expr_type_ = expr->expr_type(); min_width_ = expr->min_width(); signed_flag_ = (name[1] == 's'); - if ((arg_mode >= EXPAND) && 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: " << name << " argument width = " << expr_width_ << "." << endl; @@ -3492,6 +3502,38 @@ NetExpr* PECastType::elaborate_expr(Design*des, NetScope*scope, return 0; } +unsigned PECastSign::test_width(Design *des, NetScope *scope, width_mode_t &mode) +{ + ivl_assert(*this, base_); + + expr_width_ = sign_cast_width(des, scope, *base_, mode); + expr_type_ = base_->expr_type(); + min_width_ = base_->min_width(); + + if (!type_is_vectorable(base_->expr_type())) { + cerr << get_fileline() << ": error: Cast base expression " + "must be a vector type." << endl; + des->errors += 1; + return 0; + } + + return expr_width_; +} + +NetExpr* PECastSign::elaborate_expr(Design *des, NetScope *scope, + unsigned expr_wid, unsigned flags) const +{ + ivl_assert(*this, base_); + + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag + + NetExpr *sub = base_->elaborate_expr(des, scope, expr_width_, flags); + if (!sub) + return nullptr; + + return cast_to_width(sub, expr_wid, signed_flag_, *this); +} + unsigned PEConcat::test_width(Design*des, NetScope*scope, width_mode_t&) { expr_width_ = 0; diff --git a/parse.y b/parse.y index 1295aaa94..21e75f0ff 100644 --- a/parse.y +++ b/parse.y @@ -4045,6 +4045,16 @@ expr_primary $$ = base; } } + | signing '\'' '(' expression ')' + { PExpr*base = $4; + if (pform_requires_sv(@1, "Signing cast")) { + PECastSign*tmp = new PECastSign($1, base); + FILE_NAME(tmp, @1); + $$ = tmp; + } else { + $$ = base; + } + } /* Aggregate literals are primaries. */ diff --git a/pform_dump.cc b/pform_dump.cc index 7c80aed06..4e1be9146 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -420,6 +420,15 @@ void PECastType::dump(ostream &out) const out << ")"; } +void PECastSign::dump(ostream &out) const +{ + if (!signed_flag_) + out << "un"; + out << "signed'("; + base_->dump(out); + out << ")"; +} + void PEEvent::dump(ostream&out) const { switch (type_) {