Add support for SystemVerilog sign cast

SystemVerilog supports sign cast where it is possible to change the
signedness of an expression. Syntactical it is similar to width or type
casting, except that the keywords 'signed' or 'unsigned' are used in front
of the cast operator. E.g.

```
  logic [3:0] a = 4'b1000;
  logic [7:0] b = signed'(a); // b is 8'b11111000;
  logic signed [3:0] c = 4'b1000;
  logic signed [7:0] d = unsigned'(c); // d is 8'b00001000;
```

As noted by the LRM section 6.24.1 ("Cast operator") applying a sign cast
to an expression is equivalent to calling the $signed() and $unsigned()
system functions on the expression.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2020-10-04 17:49:23 +02:00
parent f881baeef1
commit a73ee3e3e7
5 changed files with 99 additions and 11 deletions

View File

@ -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)
{

21
PExpr.h
View File

@ -23,6 +23,7 @@
# include <string>
# include <vector>
# include <valarray>
# include <memory>
# 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<PExpr> base_;
};
/*
* This class is used for error recovery. All methods do nothing and return
* null or default values.

View File

@ -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;

10
parse.y
View File

@ -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. */

View File

@ -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_) {