From 112e1e3752e6a43f43691ce2e5d2dcaaa93acf03 Mon Sep 17 00:00:00 2001 From: Luca Colagrande Date: Tue, 6 Jan 2026 21:07:25 +0100 Subject: [PATCH] Fix overlapping case item expressions (#6825) (#6886) --- src/V3Assert.cpp | 17 ++++++-- src/V3Case.cpp | 10 +++-- test_regress/t/t_case_overlap_bad.out | 10 +---- test_regress/t/t_case_unique_overlap.py | 18 ++++++++ test_regress/t/t_case_unique_overlap.v | 56 +++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 14 deletions(-) create mode 100755 test_regress/t/t_case_unique_overlap.py create mode 100644 test_regress/t/t_case_unique_overlap.v diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 0b0702dbb..902e5501c 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -595,6 +595,7 @@ class AssertVisitor final : public VNVisitor { AstNodeExpr* propp = nullptr; for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp = VN_AS(itemp->nextp(), CaseItem)) { + AstNodeExpr* itembitp = nullptr; for (AstNodeExpr* icondp = itemp->condsp(); icondp; icondp = VN_AS(icondp->nextp(), NodeExpr)) { AstNodeExpr* onep; @@ -612,17 +613,27 @@ class AssertVisitor final : public VNVisitor { nodep->exprp()->cloneTreePure(false), icondp->cloneTreePure(false)); } + // OR together all conditions within the same case item + if (onep) { + if (itembitp) { + itembitp = new AstOr{icondp->fileline(), onep, itembitp}; + } else { + itembitp = onep; + } + } + } + if (itembitp) { if (propp) { - propp = new AstConcat{icondp->fileline(), onep, propp}; + propp = new AstConcat{itemp->fileline(), itembitp, propp}; } else { - propp = onep; + propp = itembitp; } } } // Empty case means no property if (!propp) propp = new AstConst{nodep->fileline(), AstConst::BitFalse{}}; const bool allow_none = has_default || nodep->unique0Pragma(); - // The following assertion lools as below. + // The following assertion looks as below. // if (!$onehot(propp)) begin // if (propp == '0) begin if (!allow_none) $error("none match"); end // else $error("multiple match"); diff --git a/src/V3Case.cpp b/src/V3Case.cpp index ff4fb6727..720d4c42c 100644 --- a/src/V3Case.cpp +++ b/src/V3Case.cpp @@ -244,9 +244,13 @@ class CaseVisitor final : public VNVisitor { caseItemMap[icondp] = itemp; foundHit = true; } else if (!overlappedCondp) { - firstOverlap = i; - overlappedCondp = m_valueItem[i]; - m_caseNoOverlapsAllCovered = false; + // Overlapping case item expressions in the + // same case item are legal + if (caseItemMap[m_valueItem[i]] != itemp) { + firstOverlap = i; + overlappedCondp = m_valueItem[i]; + m_caseNoOverlapsAllCovered = false; + } } } } diff --git a/test_regress/t/t_case_overlap_bad.out b/test_regress/t/t_case_overlap_bad.out index 3362705db..f53a2e97d 100644 --- a/test_regress/t/t_case_overlap_bad.out +++ b/test_regress/t/t_case_overlap_bad.out @@ -1,17 +1,11 @@ -%Warning-CASEOVERLAP: t/t_case_overlap_bad.v:20:21: Case conditions overlap (example pattern 0x6) - 20 | 3'b11?, 3'b???: v++; - | ^~~~~~ - t/t_case_overlap_bad.v:20:13: ... Location of overlapping condition - 20 | 3'b11?, 3'b???: v++; - | ^~~~~~ - ... For warning description see https://verilator.org/warn/CASEOVERLAP?v=latest - ... Use "/* verilator lint_off CASEOVERLAP */" and lint_on around source to disable this message. %Warning-CASEOVERLAP: t/t_case_overlap_bad.v:25:13: Case conditions overlap 25 | 3'b001, 3'b000: $stop; | ^~~~~~ t/t_case_overlap_bad.v:24:13: ... Location of overlapping condition 24 | 3'b00?: $stop; | ^~~~~~ + ... For warning description see https://verilator.org/warn/CASEOVERLAP?v=latest + ... Use "/* verilator lint_off CASEOVERLAP */" and lint_on around source to disable this message. %Warning-CASEOVERLAP: t/t_case_overlap_bad.v:30:13: Case conditions overlap (example pattern 0x7) 30 | 3'b11?: $stop; | ^~~~~~ diff --git a/test_regress/t/t_case_unique_overlap.py b/test_regress/t/t_case_unique_overlap.py new file mode 100755 index 000000000..e41ab0cdd --- /dev/null +++ b/test_regress/t/t_case_unique_overlap.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2026 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() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_case_unique_overlap.v b/test_regress/t/t_case_unique_overlap.v new file mode 100644 index 000000000..c31eac998 --- /dev/null +++ b/test_regress/t/t_case_unique_overlap.v @@ -0,0 +1,56 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Luca Colagrande. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + localparam logic [1:0] INST1 = 2'b0?; + localparam logic [1:0] INST2 = 2'b0?; + localparam logic [1:0] INST3 = 2'b1?; + + logic [1:0] in, out; + + always_comb begin + unique casez (in) + INST1, INST2: begin + if (in == 2'b00) out = 2'b01; + else out = 2'b00; + end + INST3: begin + out = 2'b10; + end + default: begin + out = 2'b11; + end + endcase + end + + always @ (posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] in=%x out=%x\n", $time, in, out); +`endif + if (in == 0) begin + if (out != 2'b01) $stop; + end + else if (in == 1) begin + if (out != 2'b00) $stop; + end + else if (in == 2) begin + if (out != 2'b10) $stop; + end + else if (in == 3) begin + if (out != 2'b10) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + in <= in + 1; + end + +endmodule