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)
|
||||
: 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;
|
||||
|
|
|
|||
|
|
@ -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_func normal,-g2009 ivltests gold=sv_root_func.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_string2 normal,-g2009 ivltests
|
||||
sv_string3 normal,-g2009 ivltests
|
||||
|
|
|
|||
|
|
@ -1034,6 +1034,9 @@ shift4 normal,-pallowsigned=1 ivltests
|
|||
signed5 normal,-pallowsigned=1 ivltests
|
||||
signed10 normal,-pallowsigned=1 ivltests gold=signed10.gold
|
||||
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.
|
||||
readmem-error normal,-pallowsigned=1 ivltests gold=readmem-error-vlog95.gold
|
||||
|
||||
|
|
|
|||
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