From 185bbbe681d02874796e70a1ee147f4b8dca6cbb Mon Sep 17 00:00:00 2001
From: Kazuki Sakamoto <sakamoto@splhack.org>
Date: Sun, 14 Jun 2020 15:15:59 -0700
Subject: [PATCH 1/3] static cast: support changing size and signedness

Support SystemVerilog Static Cast
- size
- signedness
- (type is not supposted yet)

Fix #535
---
 frontends/ast/ast.cc               |  1 +
 frontends/ast/ast.h                |  1 +
 frontends/ast/genrtlil.cc          | 24 ++++++++++++++++++++++++
 frontends/ast/simplify.cc          |  1 +
 frontends/verilog/verilog_lexer.l  |  2 ++
 frontends/verilog/verilog_parser.y | 19 +++++++++++++++++++
 6 files changed, 48 insertions(+)

diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 03fd272da..9520ae32c 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -95,6 +95,7 @@ std::string AST::type2str(AstNodeType type)
 	X(AST_TO_SIGNED)
 	X(AST_TO_UNSIGNED)
 	X(AST_SELFSZ)
+	X(AST_CAST_SIZE)
 	X(AST_CONCAT)
 	X(AST_REPLICATE)
 	X(AST_BIT_NOT)
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index 46864a4e1..9a5aa15f9 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -76,6 +76,7 @@ namespace AST
 		AST_TO_SIGNED,
 		AST_TO_UNSIGNED,
 		AST_SELFSZ,
+		AST_CAST_SIZE,
 		AST_CONCAT,
 		AST_REPLICATE,
 		AST_BIT_NOT,
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index 9546558aa..e878d0dd2 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -814,6 +814,16 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
 		children.at(0)->detectSignWidthWorker(sub_width_hint, sign_hint);
 		break;
 
+	case AST_CAST_SIZE:
+		while (children.at(0)->simplify(true, false, false, 1, -1, false, false)) { }
+		if (children.at(0)->type != AST_CONSTANT)
+			log_file_error(filename, location.first_line, "Static cast with non constant expression!\n");
+		children.at(1)->detectSignWidthWorker(width_hint, sign_hint);
+		width_hint = children.at(0)->bitsAsConst().as_int();
+		if (width_hint <= 0)
+			log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n");
+		break;
+
 	case AST_CONCAT:
 		for (auto child : children) {
 			sub_width_hint = 0;
@@ -1289,6 +1299,20 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
 			return sig;
 	}
 
+	// changing the size of signal can be done directly using RTLIL::SigSpec
+	case AST_CAST_SIZE: {
+			RTLIL::SigSpec size = children[0]->genRTLIL();
+			RTLIL::SigSpec sig = children[1]->genRTLIL();
+			if (!size.is_fully_const())
+				log_file_error(filename, location.first_line, "Static cast with non constant expression!\n");
+			int width = size.as_int();
+			if (width <= 0)
+				log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n");
+			sig.extend_u0(width, sign_hint);
+			is_signed = sign_hint;
+			return sig;
+		}
+
 	// concatenation of signals can be done directly using RTLIL::SigSpec
 	case AST_CONCAT: {
 			RTLIL::SigSpec sig;
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 5f026dfed..1d5dd91a7 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -950,6 +950,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 	case AST_TO_SIGNED:
 	case AST_TO_UNSIGNED:
 	case AST_SELFSZ:
+	case AST_CAST_SIZE:
 	case AST_CONCAT:
 	case AST_REPLICATE:
 	case AST_REDUCE_AND:
diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l
index e6fa6361e..6195bb149 100644
--- a/frontends/verilog/verilog_lexer.l
+++ b/frontends/verilog/verilog_lexer.l
@@ -517,6 +517,8 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ {
 "<<<" { return OP_SSHL; }
 ">>>" { return OP_SSHR; }
 
+"'" { return OP_CAST; }
+
 "::"  { return TOK_PACKAGESEP; }
 "++"  { return TOK_INCREMENT; }
 "--"  { return TOK_DECREMENT; }
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index 15c231f3b..bbf1a436b 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -298,6 +298,7 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode)
 %left '+' '-'
 %left '*' '/' '%'
 %left OP_POW
+%left OP_CAST
 %right UNARY_OPS
 
 %define parse.error verbose
@@ -3001,6 +3002,24 @@ basic_expr:
 		$$ = new AstNode(AST_LOGIC_NOT, $3);
 		SET_AST_NODE_LOC($$, @1, @3);
 		append_attr($$, $2);
+	} |
+	TOK_SIGNED OP_CAST '(' expr ')' {
+		if (!sv_mode)
+			frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode.");
+		$$ = new AstNode(AST_TO_SIGNED, $4);
+		SET_AST_NODE_LOC($$, @1, @4);
+	} |
+	TOK_UNSIGNED OP_CAST '(' expr ')' {
+		if (!sv_mode)
+			frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode.");
+		$$ = new AstNode(AST_TO_UNSIGNED, $4);
+		SET_AST_NODE_LOC($$, @1, @4);
+	} |
+	basic_expr OP_CAST '(' expr ')' {
+		if (!sv_mode)
+			frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode.");
+		$$ = new AstNode(AST_CAST_SIZE, $1, $4);
+		SET_AST_NODE_LOC($$, @1, @4);
 	};
 
 concat_list:

From 6bf75be73bc0767b5df9e3fa33a58cfe6aae9f89 Mon Sep 17 00:00:00 2001
From: Kazuki Sakamoto <sakamoto@splhack.org>
Date: Sun, 14 Jun 2020 15:26:47 -0700
Subject: [PATCH 2/3] static cast: add tests

---
 tests/svtypes/static_cast_negative.ys |  4 ++
 tests/svtypes/static_cast_nonconst.ys |  4 ++
 tests/svtypes/static_cast_simple.sv   | 64 +++++++++++++++++++++++++++
 tests/svtypes/static_cast_verilog.ys  |  4 ++
 tests/svtypes/static_cast_zero.ys     |  4 ++
 5 files changed, 80 insertions(+)
 create mode 100644 tests/svtypes/static_cast_negative.ys
 create mode 100644 tests/svtypes/static_cast_nonconst.ys
 create mode 100644 tests/svtypes/static_cast_simple.sv
 create mode 100644 tests/svtypes/static_cast_verilog.ys
 create mode 100644 tests/svtypes/static_cast_zero.ys

diff --git a/tests/svtypes/static_cast_negative.ys b/tests/svtypes/static_cast_negative.ys
new file mode 100644
index 000000000..4f9e8cf6e
--- /dev/null
+++ b/tests/svtypes/static_cast_negative.ys
@@ -0,0 +1,4 @@
+logger -expect error "Static cast with zero or negative size" 1
+read_verilog -sv <<EOT
+module top; wire [7:0] a = (-1)'(a); endmodule
+EOT
diff --git a/tests/svtypes/static_cast_nonconst.ys b/tests/svtypes/static_cast_nonconst.ys
new file mode 100644
index 000000000..72d8f9910
--- /dev/null
+++ b/tests/svtypes/static_cast_nonconst.ys
@@ -0,0 +1,4 @@
+logger -expect error "Static cast with non constant expression" 1
+read_verilog -sv <<EOT
+module top; wire [7:0] a, b = (a)'(0); endmodule
+EOT
diff --git a/tests/svtypes/static_cast_simple.sv b/tests/svtypes/static_cast_simple.sv
new file mode 100644
index 000000000..2e4ad7d2b
--- /dev/null
+++ b/tests/svtypes/static_cast_simple.sv
@@ -0,0 +1,64 @@
+module top;
+	wire [7:0] a, b, c, d;
+	assign a = 8'd16;
+	assign b = 8'd16;
+	assign c = (a * b) >> 8;
+	assign d = (16'(a) * b) >> 8;
+
+	parameter P = 16;
+
+	wire signed [7:0] s0, s1, s2;
+	wire [7:0] u0, u1, u2, u3, u4, u5, u6;
+	assign s0 = -8'd1;
+	assign s1 = 4'(s0);
+	assign s2 = 4'(unsigned'(s0));
+	assign u0 = -8'd1;
+	assign u1 = 4'(u0);
+	assign u2 = 4'(signed'(u0));
+	assign u3 = 8'(4'(s0));
+	assign u4 = 8'(4'(u0));
+	assign u5 = 8'(4'(signed'(-8'd1)));
+	assign u6 = 8'(4'(unsigned'(-8'd1)));
+
+	wire [8:0] n0, n1, n2, n3, n4, n5, n6, n7, n8, n9;
+	assign n0 = s1;
+	assign n1 = s2;
+	assign n2 = 9'(s1);
+	assign n3 = 9'(s2);
+	assign n4 = 9'(unsigned'(s1));
+	assign n5 = 9'(unsigned'(s2));
+	assign n6 = 9'(u0);
+	assign n7 = 9'(u1);
+	assign n8 = 9'(signed'(u0));
+	assign n9 = 9'(signed'(u1));
+
+	always_comb begin
+		assert(c == 8'b0000_0000);
+		assert(d == 8'b0000_0001);
+
+		assert((P + 1)'(a) == 17'b0_0000_0000_0001_0000);
+		assert((P + 1)'(d - 2) == 17'b1_1111_1111_1111_1111);
+
+		assert(s0 == 8'b1111_1111);
+		assert(s1 == 8'b1111_1111);
+		assert(s2 == 8'b0000_1111);
+		assert(u0 == 8'b1111_1111);
+		assert(u1 == 8'b0000_1111);
+		assert(u2 == 8'b1111_1111);
+		assert(u3 == 8'b1111_1111);
+		assert(u4 == 8'b0000_1111);
+		assert(u5 == 8'b1111_1111);
+		assert(u6 == 8'b0000_1111);
+
+		assert(n0 == 9'b1_1111_1111);
+		assert(n1 == 9'b0_0000_1111);
+		assert(n2 == 9'b1_1111_1111);
+		assert(n3 == 9'b0_0000_1111);
+		assert(n4 == 9'b0_1111_1111);
+		assert(n5 == 9'b0_0000_1111);
+		assert(n6 == 9'b0_1111_1111);
+		assert(n7 == 9'b0_0000_1111);
+		assert(n8 == 9'b1_1111_1111);
+		assert(n9 == 9'b0_0000_1111);
+	end
+endmodule
diff --git a/tests/svtypes/static_cast_verilog.ys b/tests/svtypes/static_cast_verilog.ys
new file mode 100644
index 000000000..fa3680b68
--- /dev/null
+++ b/tests/svtypes/static_cast_verilog.ys
@@ -0,0 +1,4 @@
+logger -expect error "Static cast is only supported in SystemVerilog mode" 1
+read_verilog <<EOT
+module top; wire [7:0] a = 1'(a); endmodule
+EOT
diff --git a/tests/svtypes/static_cast_zero.ys b/tests/svtypes/static_cast_zero.ys
new file mode 100644
index 000000000..d8335ca1b
--- /dev/null
+++ b/tests/svtypes/static_cast_zero.ys
@@ -0,0 +1,4 @@
+logger -expect error "Static cast with zero or negative size" 1
+read_verilog -sv <<EOT
+module top; wire [7:0] a = 0'(a); endmodule
+EOT

From 429d37ff41b5a058fdd0b70f23a55170a973c369 Mon Sep 17 00:00:00 2001
From: Kazuki Sakamoto <sakamoto@splhack.org>
Date: Fri, 19 Jun 2020 19:09:43 -0700
Subject: [PATCH 3/3] static cast: simplify

---
 frontends/ast/simplify.cc | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 1d5dd91a7..e2da17c09 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -3484,6 +3484,13 @@ replace_fcall_later:;
 				}
 			}
 			break;
+		case AST_CAST_SIZE:
+			if (children.at(0)->type == AST_CONSTANT && children.at(1)->type == AST_CONSTANT) {
+				int width = children[0]->bitsAsConst().as_int();
+				RTLIL::Const val = children[1]->bitsAsConst(width);
+				newNode = mkconst_bits(val.bits, children[1]->is_signed);
+			}
+			break;
 		case AST_CONCAT:
 			string_op = !children.empty();
 			for (auto it = children.begin(); it != children.end(); it++) {