diff --git a/Changes b/Changes index 49b7e80b6..e4a4d1a5a 100644 --- a/Changes +++ b/Changes @@ -17,7 +17,7 @@ Verilator 5.037 devel * Add PROCINITASSIGN on initial assignments to process variables (#2481). [Niraj Menon] * Fix filename backslash escapes in C code (#5947). * Fix C++ widths in V3Expand (#5953) (#5975). [Geza Lore] -* Fix constant propagation making upper bits Xs (#5955) (#5969). +* Fix constant propagation of post-expand stages (#5955) (#5963) (#5969) (#5972). * Fix sign extension of signed compared with unsigned case items (#5968). * Fix always processes ignoring $finish (#5971). [Hennadii Chernyshchyk] * Fix streaming to/from packed arrays (#5976). [Geza Lore] diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index bbcb8fcc3..81c93c04c 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -427,6 +427,8 @@ public: string prettyDTypeName(bool full) const override; const char* broken() const override { BROKEN_RTN(dtypep() != this); + BROKEN_RTN(v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH + && widthMin() > width()); return nullptr; } void setSignedState(const VSigning& signst) { diff --git a/src/V3Const.cpp b/src/V3Const.cpp index ccaf1d02b..dd4eadbf6 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -927,6 +927,18 @@ class ConstVisitor final : public VNVisitor { // METHODS + V3Number constNumV(AstNode* nodep) { + // Contract C width to V width (if needed, else just direct copy) + // The upper zeros in the C representation can otherwise cause + // wrong results in some operations, e.g. MulS + const V3Number& numc = VN_AS(nodep, Const)->num(); + return !numc.isNumber() ? numc : V3Number{nodep, nodep->widthMinV(), numc}; + } + V3Number toNumC(AstNode* nodep, V3Number& numv) { + // Extend V width back to C width for given node + return !numv.isNumber() ? numv : V3Number{nodep, nodep->width(), numv}; + } + bool operandConst(AstNode* nodep) { return VN_IS(nodep, Const); } bool operandAsvConst(const AstNode* nodep) { // BIASV(CONST, BIASV(CONST,...)) -> BIASV( BIASV_CONSTED(a,b), ...) @@ -1614,31 +1626,32 @@ class ConstVisitor final : public VNVisitor { VL_DO_DANGLING(replaceNum(nodep, ones), nodep); } void replaceConst(AstNodeUniop* nodep) { - V3Number num{nodep, nodep->width()}; - nodep->numberOperate(num, VN_AS(nodep->lhsp(), Const)->num()); + V3Number numv{nodep, nodep->widthMinV()}; + nodep->numberOperate(numv, constNumV(nodep->lhsp())); + const V3Number& num = toNumC(nodep, numv); UINFO(4, "UNICONST -> " << num << endl); VL_DO_DANGLING(replaceNum(nodep, num), nodep); } void replaceConst(AstNodeBiop* nodep) { - V3Number num{nodep, nodep->width()}; - nodep->numberOperate(num, VN_AS(nodep->lhsp(), Const)->num(), - VN_AS(nodep->rhsp(), Const)->num()); + V3Number numv{nodep, nodep->widthMinV()}; + nodep->numberOperate(numv, constNumV(nodep->lhsp()), constNumV(nodep->rhsp())); + const V3Number& num = toNumC(nodep, numv); UINFO(4, "BICONST -> " << num << endl); VL_DO_DANGLING(replaceNum(nodep, num), nodep); } void replaceConst(AstNodeTriop* nodep) { - V3Number num{nodep, nodep->width()}; - nodep->numberOperate(num, VN_AS(nodep->lhsp(), Const)->num(), - VN_AS(nodep->rhsp(), Const)->num(), - VN_AS(nodep->thsp(), Const)->num()); + V3Number numv{nodep, nodep->widthMinV()}; + nodep->numberOperate(numv, constNumV(nodep->lhsp()), constNumV(nodep->rhsp()), + constNumV(nodep->thsp())); + const V3Number& num = toNumC(nodep, numv); UINFO(4, "TRICONST -> " << num << endl); VL_DO_DANGLING(replaceNum(nodep, num), nodep); } void replaceConst(AstNodeQuadop* nodep) { - V3Number num{nodep, nodep->width()}; - nodep->numberOperate( - num, VN_AS(nodep->lhsp(), Const)->num(), VN_AS(nodep->rhsp(), Const)->num(), - VN_AS(nodep->thsp(), Const)->num(), VN_AS(nodep->fhsp(), Const)->num()); + V3Number numv{nodep, nodep->widthMinV()}; + nodep->numberOperate(numv, constNumV(nodep->lhsp()), constNumV(nodep->rhsp()), + constNumV(nodep->thsp()), constNumV(nodep->fhsp())); + const V3Number& num = toNumC(nodep, numv); UINFO(4, "QUADCONST -> " << num << endl); VL_DO_DANGLING(replaceNum(nodep, num), nodep); } @@ -1719,6 +1732,7 @@ class ConstVisitor final : public VNVisitor { nodep->rhsp(cp); rp->lhsp(ap); rp->rhsp(bp); + rp->dtypeFrom(nodep); // Upper widthMin more likely correct if (VN_IS(rp->lhsp(), Const) && VN_IS(rp->rhsp(), Const)) replaceConst(rp); // if (debug()) nodep->dumpTree("- repAsvConst_new: "); } @@ -1732,6 +1746,7 @@ class ConstVisitor final : public VNVisitor { nodep->rhsp(lp); lp->lhsp(lrp); lp->rhsp(rp); + lp->dtypeFrom(nodep); // Upper widthMin more likely correct // if (debug()) nodep->dumpTree("- repAsvLUp_new: "); } void replaceAsvRUp(AstNodeBiop* nodep) { @@ -1744,6 +1759,7 @@ class ConstVisitor final : public VNVisitor { nodep->rhsp(rp); rp->lhsp(lp); rp->rhsp(rrp); + rp->dtypeFrom(nodep); // Upper widthMin more likely correct // if (debug()) nodep->dumpTree("- repAsvRUp_new: "); } void replaceAndOr(AstNodeBiop* nodep) { @@ -3278,7 +3294,7 @@ class ConstVisitor final : public VNVisitor { if (argp) { AstNode* const nextp = argp->nextp(); if (VN_IS(argp, Const)) { // Convert it - const string out = VN_AS(argp, Const)->num().displayed(nodep, fmt); + const string out = constNumV(argp).displayed(nodep, fmt); UINFO(9, " DispConst: " << fmt << " -> " << out << " for " << argp << endl); // fmt = out w/ replace % with %% as it must be literal. diff --git a/src/V3Number.h b/src/V3Number.h index 85cd4d150..d092e8f96 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -639,6 +639,7 @@ public: bool isEqZero() const VL_MT_SAFE; bool isNeqZero() const; bool isBitsZero(int msb, int lsb) const; + bool isBroken(int vwidth) const; bool isEqOne() const; bool isEqAllOnes(int optwidth = 0) const; bool isCaseEq(const V3Number& rhs) const; // operator== diff --git a/test_regress/t/t_math_cv_concat.py b/test_regress/t/t_math_cv_concat.py new file mode 100755 index 000000000..4ff66dda6 --- /dev/null +++ b/test_regress/t/t_math_cv_concat.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=['--binary', '-fno-expand']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_math_cv_concat.v b/test_regress/t/t_math_cv_concat.v new file mode 100644 index 000000000..5f4b9e37d --- /dev/null +++ b/test_regress/t/t_math_cv_concat.v @@ -0,0 +1,42 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0h\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); + +module t; + // Issue #5972 + + reg clk; + reg signed [28:28] in1; + reg signed [21:8] reg_10; + + // verilator lint_off WIDTHEXPAND + always @(negedge clk) begin + // Issue #5972 + reg_10[14:8] <= {1'b1, ~((in1[28:28] & ~(in1[28:28])))}; + end + + initial begin + clk = 1; + in1 = 1'b0; + reg_10 = '0; + #2; + clk = 0; + #2; + `checkh(reg_10, 3); + + in1 = 1'b1; + clk = 1; + #2; + clk = 0; + #2; + `checkh(reg_10, 3); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_math_cv_format.py b/test_regress/t/t_math_cv_format.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_math_cv_format.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=["--binary"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_math_cv_format.v b/test_regress/t/t_math_cv_format.v new file mode 100644 index 000000000..1896c2bc3 --- /dev/null +++ b/test_regress/t/t_math_cv_format.v @@ -0,0 +1,78 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); + +module t; + wire signed [21:10] out0; + + sub sub ( + .out0(out0) + ); + + sub2 sub2 (); + + string s; + + initial begin + #20; + // Bug with sformat, so can't just number-compare + s = $sformatf("out0=%0d", out0); + `checks(s, "out0=-12"); + if (out0 > 0) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule + +module sub (out0); + reg signed [27:20] reg_4; + output wire [21:10] out0; + + initial begin + #1; + reg_4 = 0; + end + + wire [11:0] w55; + wire [11:0] w23; + // verilator lint_off WIDTHEXPAND + assign w55 = ~reg_4[20]; + // verilator lint_on WIDTHEXPAND + assign { w23[3], w23[1:0] } = 3'h0; + assign { w23[11:4], w23[2] } = { w55[11:4], w55[2] }; + assign out0 = w23; +endmodule + +module sub2; + reg [27:5] in0; + reg [26:11] in1; + wire [24:14] wire_0; + wire [26:5] out1; + wire w085; + wire w082; + wire [10:0] w092; + wire [9:0] w028; + + string s; + + initial begin + in0 = 6902127; + in1 = 10000; + #10; + s = $sformatf("out0=%0d", out1); + `checks(s, "out0=0"); + end + + assign w028 = ~ { 9'h000, in0[23] }; + assign w092[1] = 1'h0; + assign { w092[10:2], w092[0] } = w028; + assign wire_0 = w092; + assign w082 = | wire_0[18:17]; + assign w085 = w082 ? in1[11] : 1'h0; + assign out1 = { 21'h000000, w085 }; +endmodule