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:
parent
f881baeef1
commit
a73ee3e3e7
6
PExpr.cc
6
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)
|
||||
{
|
||||
|
|
|
|||
21
PExpr.h
21
PExpr.h
|
|
@ -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.
|
||||
|
|
|
|||
64
elab_expr.cc
64
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;
|
||||
|
|
|
|||
10
parse.y
10
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. */
|
||||
|
||||
|
|
|
|||
|
|
@ -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_) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue