From a650d9079fa4732a6d118f2764d5abc2522a6b37 Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Mon, 30 May 2022 16:45:39 -0400 Subject: [PATCH] verilog: fix width/sign detection for functions --- CHANGELOG | 2 ++ frontends/ast/genrtlil.cc | 12 ++++++---- tests/verilog/func_tern_hint.sv | 42 +++++++++++++++++++++++++++++++++ tests/verilog/func_tern_hint.ys | 4 ++++ 4 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 tests/verilog/func_tern_hint.sv create mode 100644 tests/verilog/func_tern_hint.ys diff --git a/CHANGELOG b/CHANGELOG index 4ee364a57..60e53aa6c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,8 @@ Yosys 0.17 .. Yosys 0.17-dev the remaining cases - Fixed size and signedness computation for expressions containing array querying functions + - Fixed size and signedness computation of functions used in ternary + expressions or case item expressions Yosys 0.16 .. Yosys 0.17 -------------------------- diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index a569c5ae2..d81c53dfb 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -1095,8 +1095,9 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (current_scope.count(str)) { // This width detection is needed for function calls which are - // unelaborated, which currently only applies to calls to recursive - // functions reached by unevaluated ternary branches. + // unelaborated, which currently applies to calls to functions + // reached via unevaluated ternary branches or used in case or case + // item expressions. const AstNode *func = current_scope.at(str); if (func->type != AST_FUNCTION) log_file_error(filename, location.first_line, "Function call to %s resolved to something that isn't a function!\n", RTLIL::unescape_id(str).c_str()); @@ -1107,8 +1108,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun break; } log_assert(wire && wire->type == AST_WIRE); - sign_hint = wire->is_signed; - width_hint = 1; + sign_hint &= wire->is_signed; + int result_width = 1; if (!wire->children.empty()) { log_assert(wire->children.size() == 1); @@ -1121,10 +1122,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (left->type != AST_CONSTANT || right->type != AST_CONSTANT) log_file_error(filename, location.first_line, "Function %s has non-constant width!", RTLIL::unescape_id(str).c_str()); - width_hint = abs(int(left->asInt(true) - right->asInt(true))); + result_width = abs(int(left->asInt(true) - right->asInt(true))); delete left; delete right; } + width_hint = max(width_hint, result_width); break; } YS_FALLTHROUGH diff --git a/tests/verilog/func_tern_hint.sv b/tests/verilog/func_tern_hint.sv new file mode 100644 index 000000000..3c58c9913 --- /dev/null +++ b/tests/verilog/func_tern_hint.sv @@ -0,0 +1,42 @@ +module top; + function automatic [30:0] func; + input integer inp; + func = { // self-determined context + ( + inp == 0 + ? -1 // causes whole ternary to be 32 bits + : func(inp - 1) // 31 bits, unsigned + ) >> 2}; + endfunction + function automatic signed [3:0] dunk; + input integer inp; + dunk = ( + inp == 0 + ? 4'hF + // shouldn't make the ternary signed + : dunk(inp - 1) + ) == -1; + endfunction + localparam A = func(0); + localparam B = func(1); + localparam C = func(2); + localparam D = func(3); + localparam X = dunk(0); + localparam Y = dunk(1); + initial begin + assert(A == 31'h3F_FFFFFF); + assert(B == 31'h0F_FFFFFF); + assert(C == 31'h03_FFFFFF); + assert(D == 31'h00_FFFFFF); + assert(X == 0); + assert(Y == 0); + end + initial begin + logic x; + case (1'b1) + dunk(0): x = 0; + default: x = 1; + endcase + assert(x); + end +endmodule diff --git a/tests/verilog/func_tern_hint.ys b/tests/verilog/func_tern_hint.ys new file mode 100644 index 000000000..ab8a1e032 --- /dev/null +++ b/tests/verilog/func_tern_hint.ys @@ -0,0 +1,4 @@ +read_verilog -sv func_tern_hint.sv +proc +opt +sat -verify -seq 1 -prove-asserts -show-all