Merge pull request #763 from larsclausen/sign-cast

Add support for SystemVerilog sign cast
This commit is contained in:
Stephen Williams 2022-09-14 09:23:03 -07:00 committed by GitHub
commit 5a774a9cab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 284 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;

View File

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

View File

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

View File

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

View File

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

View File

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