Fix false illegally-wrapped-around error for signed enums whose auto-incremented values cross zero. (#7268)
This commit is contained in:
parent
f4b09cffa7
commit
ef5281ab73
|
|
@ -2875,6 +2875,7 @@ class WidthVisitor final : public VNVisitor {
|
|||
// Assign missing values
|
||||
V3Number num(nodep, nodep->width(), 0);
|
||||
const V3Number one{nodep, nodep->width(), 1};
|
||||
bool wrapAround = false;
|
||||
std::map<const V3Number, AstEnumItem*> inits;
|
||||
for (AstEnumItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), EnumItem)) {
|
||||
|
|
@ -2890,9 +2891,10 @@ class WidthVisitor final : public VNVisitor {
|
|||
continue;
|
||||
}
|
||||
// TODO IEEE says assigning sized number that is not same size as enum is illegal
|
||||
wrapAround = false; // Explicit value resets wrap-around tracking
|
||||
}
|
||||
if (!itemp->valuep()) {
|
||||
if (num.isEqZero() && itemp != nodep->itemsp()) {
|
||||
if (wrapAround) {
|
||||
itemp->v3error("Enum value illegally wrapped around (IEEE 1800-2023 6.19)");
|
||||
}
|
||||
if (num.isFourState()) {
|
||||
|
|
@ -2918,7 +2920,16 @@ class WidthVisitor final : public VNVisitor {
|
|||
<< otherp->warnOther() << "... Location of original declaration\n"
|
||||
<< otherp->warnContextSecondary());
|
||||
}
|
||||
// Detect wrap-around after increment for the next auto-valued item.
|
||||
// For signed types, overflow is positive-to-negative (e.g., INT_MAX+1).
|
||||
// For unsigned types, overflow wraps to zero (e.g., UINT_MAX+1).
|
||||
const bool prevNeg = constp->num().isNegative();
|
||||
num.opAdd(one, constp->num());
|
||||
if (basicp->isSigned()) {
|
||||
wrapAround = !prevNeg && num.isNegative();
|
||||
} else {
|
||||
wrapAround = num.isEqZero();
|
||||
}
|
||||
}
|
||||
}
|
||||
void visit(AstEnumItem* nodep) override {
|
||||
|
|
|
|||
|
|
@ -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('simulator')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 PlanV GmbH
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test that signed enum types auto-incrementing through zero are accepted.
|
||||
// Previously Verilator falsely rejected these with "illegally wrapped around".
|
||||
|
||||
module t;
|
||||
|
||||
// Case 1: signed enum crossing zero (the original bug)
|
||||
typedef enum int {
|
||||
CROSS_NEG2 = -2,
|
||||
CROSS_NEG1, // -1
|
||||
CROSS_ZERO, // 0
|
||||
CROSS_POS1 // 1
|
||||
} cross_zero_e;
|
||||
|
||||
// Case 2: signed enum starting at zero
|
||||
typedef enum int {
|
||||
START_ZERO = 0,
|
||||
START_ONE, // 1
|
||||
START_TWO // 2
|
||||
} start_zero_e;
|
||||
|
||||
// Case 3: signed enum all negative
|
||||
typedef enum int {
|
||||
ALL_NEG3 = -3,
|
||||
ALL_NEG2, // -2
|
||||
ALL_NEG1 // -1
|
||||
} all_neg_e;
|
||||
|
||||
// Case 4: signed enum starting at large negative, crossing zero
|
||||
typedef enum logic signed [7:0] {
|
||||
WIDE_NEG3 = -8'sd3,
|
||||
WIDE_NEG2, // -2
|
||||
WIDE_NEG1, // -1
|
||||
WIDE_ZERO, // 0
|
||||
WIDE_POS1, // 1
|
||||
WIDE_POS2 // 2
|
||||
} wide_cross_e;
|
||||
|
||||
// Case 5: signed enum single value at zero
|
||||
typedef enum int {
|
||||
SINGLE_ZERO = 0
|
||||
} single_zero_e;
|
||||
|
||||
// Case 6: signed enum starting at -1, crossing zero
|
||||
typedef enum int {
|
||||
FROM_NEG1 = -1,
|
||||
FROM_ZERO, // 0
|
||||
FROM_POS1 // 1
|
||||
} from_neg1_e;
|
||||
|
||||
initial begin
|
||||
// Case 1: crossing zero
|
||||
if (CROSS_NEG2 !== -2) $stop;
|
||||
if (CROSS_NEG1 !== -1) $stop;
|
||||
if (CROSS_ZERO !== 0) $stop;
|
||||
if (CROSS_POS1 !== 1) $stop;
|
||||
|
||||
// Case 2: starting at zero
|
||||
if (START_ZERO !== 0) $stop;
|
||||
if (START_ONE !== 1) $stop;
|
||||
if (START_TWO !== 2) $stop;
|
||||
|
||||
// Case 3: all negative
|
||||
if (ALL_NEG3 !== -3) $stop;
|
||||
if (ALL_NEG2 !== -2) $stop;
|
||||
if (ALL_NEG1 !== -1) $stop;
|
||||
|
||||
// Case 4: wider signed type crossing zero
|
||||
if (WIDE_NEG3 !== -8'sd3) $stop;
|
||||
if (WIDE_NEG2 !== -8'sd2) $stop;
|
||||
if (WIDE_NEG1 !== -8'sd1) $stop;
|
||||
if (WIDE_ZERO !== 8'sd0) $stop;
|
||||
if (WIDE_POS1 !== 8'sd1) $stop;
|
||||
if (WIDE_POS2 !== 8'sd2) $stop;
|
||||
|
||||
// Case 5: single zero
|
||||
if (SINGLE_ZERO !== 0) $stop;
|
||||
|
||||
// Case 6: from -1 crossing zero
|
||||
if (FROM_NEG1 !== -1) $stop;
|
||||
if (FROM_ZERO !== 0) $stop;
|
||||
if (FROM_POS1 !== 1) $stop;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue