Merge pull request #763 from larsclausen/sign-cast
Add support for SystemVerilog sign cast
This commit is contained in:
commit
5a774a9cab
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)
|
PEBComp::PEBComp(char op, PExpr*l, PExpr*r)
|
||||||
: PEBinary(op, l, r)
|
: PEBinary(op, l, r)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
21
PExpr.h
21
PExpr.h
|
|
@ -23,6 +23,7 @@
|
||||||
# include <string>
|
# include <string>
|
||||||
# include <vector>
|
# include <vector>
|
||||||
# include <valarray>
|
# include <valarray>
|
||||||
|
# include <memory>
|
||||||
# include "netlist.h"
|
# include "netlist.h"
|
||||||
# include "verinum.h"
|
# include "verinum.h"
|
||||||
# include "LineInfo.h"
|
# include "LineInfo.h"
|
||||||
|
|
@ -1012,6 +1013,26 @@ class PECastType : public PExpr {
|
||||||
PExpr* base_;
|
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
|
* This class is used for error recovery. All methods do nothing and return
|
||||||
* null or default values.
|
* 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;
|
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,
|
NetExpr*PEBShift::elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp,
|
||||||
unsigned expr_wid) const
|
unsigned expr_wid) const
|
||||||
{
|
{
|
||||||
|
|
@ -1193,21 +1213,11 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope,
|
||||||
if (expr == 0)
|
if (expr == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// The argument type/width is self-determined, but affects
|
expr_width_ = sign_cast_width(des, scope, *expr, mode);
|
||||||
// the result width.
|
|
||||||
width_mode_t arg_mode = SIZED;
|
|
||||||
expr_width_ = expr->test_width(des, scope, arg_mode);
|
|
||||||
expr_type_ = expr->expr_type();
|
expr_type_ = expr->expr_type();
|
||||||
min_width_ = expr->min_width();
|
min_width_ = expr->min_width();
|
||||||
signed_flag_ = (name[1] == 's');
|
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)
|
if (debug_elaborate)
|
||||||
cerr << get_fileline() << ": debug: " << name
|
cerr << get_fileline() << ": debug: " << name
|
||||||
<< " argument width = " << expr_width_ << "." << endl;
|
<< " argument width = " << expr_width_ << "." << endl;
|
||||||
|
|
@ -3492,6 +3502,38 @@ NetExpr* PECastType::elaborate_expr(Design*des, NetScope*scope,
|
||||||
return 0;
|
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&)
|
unsigned PEConcat::test_width(Design*des, NetScope*scope, width_mode_t&)
|
||||||
{
|
{
|
||||||
expr_width_ = 0;
|
expr_width_ = 0;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Check that sign casts have the expected results when the value gets width
|
||||||
|
// extended.
|
||||||
|
|
||||||
|
module test;
|
||||||
|
bit failed = 1'b0;
|
||||||
|
reg [7:0] val;
|
||||||
|
reg signed [7:0] sval;
|
||||||
|
|
||||||
|
`define check(val, exp) \
|
||||||
|
if (exp !== val) begin \
|
||||||
|
$display("FAILED(%0d). Got %b, expected %b.", `__LINE__, val, exp); \
|
||||||
|
failed = 1'b1; \
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
// An unsized number has an implicit width of integer width.
|
||||||
|
val = unsigned'(-4);
|
||||||
|
`check(val, 8'hfc);
|
||||||
|
|
||||||
|
val = unsigned'(-4'sd4);
|
||||||
|
`check(val, 8'h0c);
|
||||||
|
|
||||||
|
sval = signed'(4'hc);
|
||||||
|
`check(sval, -4);
|
||||||
|
|
||||||
|
if (!failed) begin
|
||||||
|
$display("PASSED");
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Check that unsigned arguments to a sign cast are evaluated as self-determined.
|
||||||
|
|
||||||
|
module test;
|
||||||
|
|
||||||
|
reg [3:0] op1;
|
||||||
|
reg [2:0] op2;
|
||||||
|
reg [7:0] result;
|
||||||
|
bit failed = 1'b0;
|
||||||
|
|
||||||
|
`define check(val, exp) \
|
||||||
|
if (exp !== val) begin \
|
||||||
|
$display("FAILED(%0d). Got %b, expected %b.", `__LINE__, val, exp); \
|
||||||
|
failed = 1'b1; \
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
// Addition tests
|
||||||
|
op1 = 4'b1111; op2 = 3'b111;
|
||||||
|
result = 8'sd0 + signed'(op1 + op2);
|
||||||
|
`check(result, 8'b00000110);
|
||||||
|
result = 8'sd0 + unsigned'(op1 + op2);
|
||||||
|
`check(result, 8'b00000110);
|
||||||
|
|
||||||
|
op1 = 4'b1000; op2 = 3'b011;
|
||||||
|
result = 8'sd0 + signed'(op1 + op2);
|
||||||
|
`check(result, 8'b11111011);
|
||||||
|
result = 8'sd0 + unsigned'(op1 + op2);
|
||||||
|
`check(result, 8'b00001011);
|
||||||
|
|
||||||
|
// Multiply tests
|
||||||
|
op1 = 4'b0101; op2 = 3'b100;
|
||||||
|
result = 8'sd0 + signed'(op1 * op2);
|
||||||
|
`check(result, 8'b00000100);
|
||||||
|
result = 8'sd0 + unsigned'(op1 * op2);
|
||||||
|
`check(result, 8'b00000100);
|
||||||
|
|
||||||
|
op1 = 4'b0010; op2 = 3'b100;
|
||||||
|
result = 8'sd0 + signed'(op1 * op2);
|
||||||
|
`check(result, 8'b11111000);
|
||||||
|
result = 8'sd0 + unsigned'(op1 * op2);
|
||||||
|
`check(result, 8'b00001000);
|
||||||
|
|
||||||
|
// Left ASR tests
|
||||||
|
op1 = 4'b1010;
|
||||||
|
result = 8'sd0 + signed'(op1 <<< 1);
|
||||||
|
`check(result, 8'b00000100);
|
||||||
|
result = 8'sd0 + unsigned'(op1 <<< 1);
|
||||||
|
`check(result, 8'b00000100);
|
||||||
|
|
||||||
|
op1 = 4'b0101;
|
||||||
|
result = 8'sd0 + signed'(op1 <<< 1);
|
||||||
|
`check(result, 8'b11111010);
|
||||||
|
result = 8'sd0 + unsigned'(op1 <<< 1);
|
||||||
|
`check(result, 8'b00001010);
|
||||||
|
|
||||||
|
// Right ASR tests
|
||||||
|
op1 = 4'b1010;
|
||||||
|
result = 8'sd0 + signed'(op1 >>> 1);
|
||||||
|
`check(result, 8'b00000101);
|
||||||
|
result = 8'sd0 + unsigned'(op1 >>> 1);
|
||||||
|
`check(result, 8'b00000101);
|
||||||
|
|
||||||
|
op1 = 4'b1010;
|
||||||
|
result = 8'sd0 + signed'(op1 >>> 0);
|
||||||
|
`check(result, 8'b11111010);
|
||||||
|
result = 8'sd0 + unsigned'(op1 >>> 0);
|
||||||
|
`check(result, 8'b00001010);
|
||||||
|
|
||||||
|
if (!failed) begin
|
||||||
|
$display("PASSED");
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// Check that signed arguments to a sign cast are evaluated as self-determined.
|
||||||
|
|
||||||
|
module test;
|
||||||
|
|
||||||
|
reg signed [3:0] op1;
|
||||||
|
reg signed [2:0] op2;
|
||||||
|
reg [7:0] result;
|
||||||
|
bit failed = 1'b0;
|
||||||
|
|
||||||
|
`define check(val, exp) \
|
||||||
|
if (exp !== val) begin \
|
||||||
|
$display("FAILED(%0d). Got %b, expected %b.", `__LINE__, val, exp); \
|
||||||
|
failed = 1'b1; \
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
|
||||||
|
// Addition tests
|
||||||
|
op1 = 4'b1111; op2 = 3'b111;
|
||||||
|
result = 8'sd0 + signed'(op1 + op2);
|
||||||
|
`check(result, 8'b11111110);
|
||||||
|
result = 8'sd0 + unsigned'(op1 + op2);
|
||||||
|
`check(result, 8'b00001110);
|
||||||
|
|
||||||
|
op1 = 4'b1000; op2 = 3'b011;
|
||||||
|
result = 8'sd0 + signed'(op1 + op2);
|
||||||
|
`check(result, 8'b11111011);
|
||||||
|
result = 8'sd0 + unsigned'(op1 + op2);
|
||||||
|
`check(result, 8'b00001011);
|
||||||
|
|
||||||
|
// Multiply tests
|
||||||
|
op1 = 4'b0101; op2 = 3'b100;
|
||||||
|
result = 8'sd0 + signed'(op1 * op2);
|
||||||
|
`check(result, 8'b11111100);
|
||||||
|
result = 8'sd0 + unsigned'(op1 * op2);
|
||||||
|
`check(result, 8'b00001100);
|
||||||
|
|
||||||
|
op1 = 4'b0010; op2 = 3'b100;
|
||||||
|
result = 8'sd0 + signed'(op1 * op2);
|
||||||
|
`check(result, 8'b11111000);
|
||||||
|
result = 8'sd0 + unsigned'(op1 * op2);
|
||||||
|
`check(result, 8'b00001000);
|
||||||
|
|
||||||
|
// Left ASR tests
|
||||||
|
op1 = 4'b1010;
|
||||||
|
result = 8'sd0 + signed'(op1 <<< 1);
|
||||||
|
`check(result, 8'b00000100);
|
||||||
|
result = 8'sd0 + unsigned'(op1 <<< 1);
|
||||||
|
`check(result, 8'b00000100);
|
||||||
|
|
||||||
|
op1 = 4'b0101;
|
||||||
|
result = 8'sd0 + signed'(op1 <<< 1);
|
||||||
|
`check(result, 8'b11111010);
|
||||||
|
result = 8'sd0 + unsigned'(op1 <<< 1);
|
||||||
|
`check(result, 8'b00001010);
|
||||||
|
|
||||||
|
// Right ASR tests
|
||||||
|
op1 = 4'b0101;
|
||||||
|
result = 8'sd0 + signed'(op1 >>> 1);
|
||||||
|
`check(result, 8'b00000010);
|
||||||
|
result = 8'sd0 + unsigned'(op1 >>> 1);
|
||||||
|
`check(result, 8'b00000010);
|
||||||
|
|
||||||
|
op1 = 4'b1010;
|
||||||
|
result = 8'sd0 + signed'(op1 >>> 1);
|
||||||
|
`check(result, 8'b11111101);
|
||||||
|
result = 8'sd0 + unsigned'(op1 >>> 1);
|
||||||
|
`check(result, 8'b00001101);
|
||||||
|
|
||||||
|
if (!failed) begin
|
||||||
|
$display("PASSED");
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
@ -628,6 +628,9 @@ sv_queue_vec_fail CE,-g2009 ivltests gold=sv_queue_vec_fail.gold
|
||||||
sv_root_class normal,-g2009 ivltests gold=sv_root_class.gold
|
sv_root_class normal,-g2009 ivltests gold=sv_root_class.gold
|
||||||
sv_root_func normal,-g2009 ivltests gold=sv_root_func.gold
|
sv_root_func normal,-g2009 ivltests gold=sv_root_func.gold
|
||||||
sv_root_task normal,-g2009 ivltests gold=sv_root_task.gold
|
sv_root_task normal,-g2009 ivltests gold=sv_root_task.gold
|
||||||
|
sv_sign_cast1 normal,-g2005-sv ivltests
|
||||||
|
sv_sign_cast2 normal,-g2005-sv ivltests
|
||||||
|
sv_sign_cast3 normal,-g2005-sv ivltests
|
||||||
sv_string1 normal,-g2009 ivltests
|
sv_string1 normal,-g2009 ivltests
|
||||||
sv_string2 normal,-g2009 ivltests
|
sv_string2 normal,-g2009 ivltests
|
||||||
sv_string3 normal,-g2009 ivltests
|
sv_string3 normal,-g2009 ivltests
|
||||||
|
|
|
||||||
|
|
@ -1034,6 +1034,9 @@ shift4 normal,-pallowsigned=1 ivltests
|
||||||
signed5 normal,-pallowsigned=1 ivltests
|
signed5 normal,-pallowsigned=1 ivltests
|
||||||
signed10 normal,-pallowsigned=1 ivltests gold=signed10.gold
|
signed10 normal,-pallowsigned=1 ivltests gold=signed10.gold
|
||||||
signed13 normal,-pallowsigned=1 ivltests
|
signed13 normal,-pallowsigned=1 ivltests
|
||||||
|
sv_sign_cast1 normal,-g2005-sv,-pallowsigned=1 ivltests
|
||||||
|
sv_sign_cast2 normal,-g2005-sv,-pallowsigned=1 ivltests
|
||||||
|
sv_sign_cast3 normal,-g2005-sv,-pallowsigned=1 ivltests
|
||||||
# Also tests have different output because of file name/line, etc. differences.
|
# Also tests have different output because of file name/line, etc. differences.
|
||||||
readmem-error normal,-pallowsigned=1 ivltests gold=readmem-error-vlog95.gold
|
readmem-error normal,-pallowsigned=1 ivltests gold=readmem-error-vlog95.gold
|
||||||
|
|
||||||
|
|
|
||||||
10
parse.y
10
parse.y
|
|
@ -4045,6 +4045,16 @@ expr_primary
|
||||||
$$ = base;
|
$$ = 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. */
|
/* Aggregate literals are primaries. */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -420,6 +420,15 @@ void PECastType::dump(ostream &out) const
|
||||||
out << ")";
|
out << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PECastSign::dump(ostream &out) const
|
||||||
|
{
|
||||||
|
if (!signed_flag_)
|
||||||
|
out << "un";
|
||||||
|
out << "signed'(";
|
||||||
|
base_->dump(out);
|
||||||
|
out << ")";
|
||||||
|
}
|
||||||
|
|
||||||
void PEEvent::dump(ostream&out) const
|
void PEEvent::dump(ostream&out) const
|
||||||
{
|
{
|
||||||
switch (type_) {
|
switch (type_) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue