From 82dab7909699c8f0495d0cf0d1d0a4aed291d963 Mon Sep 17 00:00:00 2001 From: Nikolai Kumar Date: Fri, 7 Aug 2026 01:48:51 -0500 Subject: [PATCH] Fix wildcard equality against a 4-state constant in an assertion (#8056) (#8057) --- src/V3Const.cpp | 4 ++ test_regress/t/t_assert_sampled_const.py | 18 ++++++ test_regress/t/t_assert_sampled_const.v | 74 ++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100755 test_regress/t/t_assert_sampled_const.py create mode 100644 test_regress/t/t_assert_sampled_const.v diff --git a/src/V3Const.cpp b/src/V3Const.cpp index f9eac2dfe..47c7bbd13 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -4585,6 +4585,10 @@ class ConstVisitor final : public VNVisitor { // Custom // Implied by AstIsUnbounded::numberOperate: V("AstIsUnbounded{$lhsp.castConst}", "replaceNum(nodep, 0)"); TREEOPV("AstIsUnbounded{$lhsp.castUnbounded}", "replaceNum(nodep, 1)"); + // Sampled value functions of a constant. + // $rose/$fell/$stable/$changed are lowered to $past by V3AssertPre, so they fold via AstPast + TREEOPV("AstSampled{$exprp.castConst}", "replaceWChild(nodep, VN_AS(nodep->exprp(), NodeExpr))"); + TREEOPV("AstPast{$exprp.castConst, !$ticksp}", "replaceWChild(nodep, nodep->exprp())"); // clang-format on // Possible futures: diff --git a/test_regress/t/t_assert_sampled_const.py b/test_regress/t/t_assert_sampled_const.py new file mode 100755 index 000000000..2351d6963 --- /dev/null +++ b/test_regress/t/t_assert_sampled_const.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_assert_sampled_const.v b/test_regress/t/t_assert_sampled_const.v new file mode 100644 index 000000000..7714f5f89 --- /dev/null +++ b/test_regress/t/t_assert_sampled_const.v @@ -0,0 +1,74 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Nikolai Kumar +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t ( + input logic clk +); + + localparam OP_UNTYPED = 3'b000; + localparam logic [2:0] OP_LOGIC = 3'b011; + localparam logic [2:0] OP_WILD = 3'b1x1; + + logic [4:0] cyc = 0; + logic [2:0] opcode; + + int hit_untyped = 0; + int hit_logic = 0; + int hit_wild = 0; + int hit_sampled = 0; + int hit_past = 0; + int hit_past_n = 0; + int hit_stable = 0; + int hit_norose = 0; + int hit_nofell = 0; + int hit_nochanged = 0; + int hit_wild_samp = 0; + + always @(posedge clk) if (cyc != 5'd31) cyc <= cyc + 1; + + assign opcode = (cyc < 16) ? cyc[2:0] : 3'b010; + + assert property (@(posedge clk) opcode inside {OP_UNTYPED}) hit_untyped++; + assert property (@(posedge clk) opcode inside {OP_LOGIC}) hit_logic++; + assert property (@(posedge clk) opcode ==? OP_WILD) hit_wild++; + + assert property (@(posedge clk) $sampled(OP_LOGIC) == OP_LOGIC) hit_sampled++; + assert property (@(posedge clk) $past(OP_LOGIC) == OP_LOGIC) hit_past++; + assert property (@(posedge clk) $past(OP_LOGIC, 3) == OP_LOGIC) hit_past_n++; + assert property (@(posedge clk) $stable(OP_LOGIC)) hit_stable++; + assert property (@(posedge clk) !$rose(OP_LOGIC)) hit_norose++; + assert property (@(posedge clk) !$fell(OP_LOGIC)) hit_nofell++; + assert property (@(posedge clk) !$changed(OP_LOGIC)) hit_nochanged++; + + assert property (@(posedge clk) $sampled(OP_WILD) === OP_WILD) hit_wild_samp++; + + always @(posedge clk) begin + if (cyc == 24) begin + `checkd(hit_untyped, 2); + `checkd(hit_logic, 2); + `checkd(hit_wild, 4); + + `checkd(hit_sampled, 24); + `checkd(hit_wild_samp, 24); + `checkd(hit_nofell, 24); + + `checkd(hit_past, 23); + `checkd(hit_stable, 23); + `checkd(hit_norose, 23); + `checkd(hit_nochanged, 23); + + `checkd(hit_past_n, 21); + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule