Merge 712496671b into 0df0064d64
This commit is contained in:
commit
f270402871
|
|
@ -8111,13 +8111,16 @@ class WidthVisitor final : public VNVisitor {
|
|||
// For sformatf's with constant format, iterate/check arguments
|
||||
UASSERT_OBJ(!nodep->exprFormat(), nodep, "Assumes constant format");
|
||||
bool inPct = false;
|
||||
string fmtMods;
|
||||
AstNodeExpr* argp = nodep->exprsp();
|
||||
string newFormat;
|
||||
for (char ch : nodep->text()) {
|
||||
if (!inPct && ch == '%') {
|
||||
inPct = true;
|
||||
fmtMods = "";
|
||||
newFormat += ch;
|
||||
} else if (inPct && (std::isdigit(ch) || ch == '.' || ch == '-')) {
|
||||
fmtMods += ch;
|
||||
newFormat += ch;
|
||||
} else if (!inPct) { // Normal text
|
||||
newFormat += ch;
|
||||
|
|
@ -8125,7 +8128,7 @@ class WidthVisitor final : public VNVisitor {
|
|||
inPct = false;
|
||||
AstNodeExpr* const nextp = argp ? VN_AS(argp->nextp(), NodeExpr) : nullptr;
|
||||
AstSFormatArg* const fargp = VN_CAST(argp, SFormatArg); // May not exist yet
|
||||
AstNodeExpr* const subargp = fargp ? fargp->exprp() : argp;
|
||||
AstNodeExpr* subargp = fargp ? fargp->exprp() : argp;
|
||||
const AstNodeDType* const dtypep
|
||||
= subargp ? subargp->dtypep()->skipRefp() : nullptr;
|
||||
ch = std::tolower(ch);
|
||||
|
|
@ -8182,7 +8185,50 @@ class WidthVisitor final : public VNVisitor {
|
|||
}
|
||||
break;
|
||||
case 'p': // FALLTHRU
|
||||
case 's': // FALLTHRU
|
||||
case 's':
|
||||
// Keep enum `%p`/`%s` behavior aligned with enum.name():
|
||||
// valid enum values print the mnemonic; invalid values print numeric fallback.
|
||||
if (subargp) {
|
||||
AstEnumDType* enumDtp
|
||||
= VN_CAST(subargp->dtypep()->skipRefToEnump(), EnumDType);
|
||||
if (!enumDtp) {
|
||||
if (const AstVarRef* const varrefp = VN_CAST(subargp, VarRef)) {
|
||||
enumDtp = VN_CAST(varrefp->varp()->dtypep()->skipRefToEnump(),
|
||||
EnumDType);
|
||||
}
|
||||
}
|
||||
if (enumDtp) {
|
||||
string fallbackFormat = "%0d";
|
||||
if (ch == 'p') {
|
||||
bool widthSet = false;
|
||||
size_t width = 0;
|
||||
for (const char mod : fmtMods) {
|
||||
if (std::isdigit(mod)) {
|
||||
widthSet = true;
|
||||
width = width * 10 + (mod - '0');
|
||||
}
|
||||
}
|
||||
if (widthSet && width == 0) fallbackFormat = "'h%0h";
|
||||
}
|
||||
AstNodeExpr* const newp = new AstCond{
|
||||
subargp->fileline(),
|
||||
enumTestValid(subargp->cloneTreePure(false), enumDtp),
|
||||
enumSelect(subargp->cloneTreePure(false), enumDtp,
|
||||
VAttrType::ENUM_NAME),
|
||||
new AstSFormatF{subargp->fileline(), fallbackFormat, true,
|
||||
subargp->cloneTreePure(false)}};
|
||||
if (fargp) {
|
||||
fargp->formatAttr(VFormatAttr::COMPLEX);
|
||||
subargp->replaceWith(newp);
|
||||
} else {
|
||||
subargp->replaceWith(new AstSFormatArg{
|
||||
subargp->fileline(), VFormatAttr::COMPLEX, newp});
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(subargp), subargp);
|
||||
}
|
||||
}
|
||||
argp = nextp;
|
||||
break;
|
||||
default: // Most operators, just move to next argument
|
||||
argp = nextp;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -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,127 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// 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
|
||||
|
||||
module t;
|
||||
typedef enum logic [1:0] {
|
||||
E0 = 0,
|
||||
E1 = 1,
|
||||
E2 = 2
|
||||
} my_e;
|
||||
|
||||
my_e e;
|
||||
task check(input string got, input string exp);
|
||||
if (got != exp) begin
|
||||
$write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__, `__LINE__, got, exp);
|
||||
$stop;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
begin
|
||||
my_e it;
|
||||
string names_p;
|
||||
string names_s;
|
||||
string vals_d;
|
||||
names_p = "";
|
||||
names_s = "";
|
||||
vals_d = "";
|
||||
for (it = it.first; ; it = it.next) begin
|
||||
if (names_p != "") begin
|
||||
names_p = {names_p, ","};
|
||||
names_s = {names_s, ","};
|
||||
vals_d = {vals_d, ","};
|
||||
end
|
||||
names_p = {names_p, $sformatf("%p", it)};
|
||||
names_s = {names_s, $sformatf("%s", it)};
|
||||
vals_d = {vals_d, $sformatf("%0d", it)};
|
||||
if (it == it.last) break;
|
||||
end
|
||||
check(names_p, "E0,E1,E2");
|
||||
check(names_s, "E0,E1,E2");
|
||||
check(vals_d, "0,1,2");
|
||||
end
|
||||
|
||||
// Valid enum values print mnemonic for %p/%s.
|
||||
e = E0;
|
||||
check($sformatf("%p", e), "E0");
|
||||
check($sformatf("%s", e), "E0");
|
||||
|
||||
e = E1;
|
||||
check($sformatf("%p", e), "E1");
|
||||
check($sformatf("%P", e), "E1");
|
||||
check($sformatf("%0p", e), "E1");
|
||||
check($sformatf("%s", e), "E1");
|
||||
check($sformatf("%S", e), "E1");
|
||||
check($sformatf("%d", e), "1");
|
||||
check($sformatf("%0d", e), "1");
|
||||
check($sformatf("%h", e), "1");
|
||||
check($sformatf("%0h", e), "1");
|
||||
check($sformatf("%b", e), "01");
|
||||
check($sformatf("%0b", e), "1");
|
||||
check($sformatf("%o", e), "1");
|
||||
check($sformatf("%0o", e), "1");
|
||||
check($sformatf("%x", e), "1");
|
||||
check($sformatf("%0x", e), "1");
|
||||
|
||||
e = E2;
|
||||
check($sformatf("%p", e), "E2");
|
||||
check($sformatf("%s", e), "E2");
|
||||
check($sformatf("%s|%p", e, e), "E2|E2");
|
||||
check($sformatf("%4p", e), "E2");
|
||||
check($sformatf("%-4p", e), "E2");
|
||||
check($sformatf("%d", e), "2");
|
||||
check($sformatf("%h", e), "2");
|
||||
check($sformatf("%b", e), "10");
|
||||
check($sformatf("%0b", e), "10");
|
||||
check($sformatf("%o", e), "2");
|
||||
check($sformatf("%x", e), "2");
|
||||
check($sformatf("%4d", e), " 2");
|
||||
check($sformatf("%04d", e), "0002");
|
||||
check($sformatf("%4h", e), "0002");
|
||||
check($sformatf("%-4s", e), "E2 ");
|
||||
check($sformatf("%4s", e), " E2");
|
||||
// `%p`/`%s` in non-terminal positions with mixed formatters.
|
||||
check($sformatf("%0d:%s:%0d", 9, e, 7), "9:E2:7");
|
||||
check($sformatf("%s %h %p", e, 4'hA, e), "E2 a E2");
|
||||
check($sformatf("pre %% %s post", e), "pre % E2 post");
|
||||
// Complex enum expressions (non-var-ref) in format args.
|
||||
check($sformatf("%s", (1'b1 ? E2 : E0)), "E2");
|
||||
// Exercise display/write-family formatting path in addition to $sformatf checks.
|
||||
$display("display-valid:%s:%0d:%p", e, 7, e);
|
||||
$write("write-valid:%s:%0d:%p\n", e, 8, e);
|
||||
// Invalid enum values fall back to numeric formatting for %p/%s.
|
||||
e = my_e'(3);
|
||||
check($sformatf("%p", e), "3");
|
||||
check($sformatf("%P", e), "3");
|
||||
check($sformatf("%0p", e), "'h3");
|
||||
check($sformatf("%s", e), "3");
|
||||
check($sformatf("%S", e), "3");
|
||||
check($sformatf("%4p", e), "3");
|
||||
check($sformatf("%4s", e), " 3");
|
||||
check($sformatf("%d", e), "3");
|
||||
check($sformatf("%0d", e), "3");
|
||||
check($sformatf("%h", e), "3");
|
||||
check($sformatf("%0h", e), "3");
|
||||
check($sformatf("%b", e), "11");
|
||||
check($sformatf("%0b", e), "11");
|
||||
check($sformatf("%o", e), "3");
|
||||
check($sformatf("%x", e), "3");
|
||||
// Non-terminal invalid-value fallback with mixed formatters.
|
||||
check($sformatf("%0d:%p:%0d", 9, e, 7), "9:3:7");
|
||||
check($sformatf("%s %h %p", e, 4'hA, e), "3 a 3");
|
||||
check($sformatf("pre %% %s post", e), "pre % 3 post");
|
||||
check($sformatf("%s|%p", e, e), "3|3");
|
||||
check($sformatf("%s", (1'b1 ? my_e'(3) : E0)), "3");
|
||||
check($sformatf("%p", (1'b0 ? E0 : my_e'(3))), "3");
|
||||
$display("display-invalid:%s:%0d:%p", e, 7, e);
|
||||
$write("write-invalid:%s:%0d:%p\n", e, 8, e);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue