diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 0c8b1cc0b..1db8feecd 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -233,6 +233,7 @@ Steven Hugg Szymon Gizler Sören Tempel Teng Huang +Thomas Aldrian Thomas Dybdahl Ahle Tim Hutt Tim Snyder diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 96ae9ee88..1009c3234 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1203,6 +1203,7 @@ class AstModportVarRef final : public AstNode { // A input/output/etc variable referenced under a modport // The storage for the variable itself is inside the interface, thus this is a reference // PARENT: AstModport + // @astgen op1 := exprp : Optional[AstNodeExpr] // // @astgen ptr := m_varp : Optional[AstVar] // Link to the actual Var string m_name; // Name of the variable referenced @@ -1212,6 +1213,13 @@ public: : ASTGEN_SUPER_ModportVarRef(fl) , m_name{name} , m_direction{direction} {} + AstModportVarRef(FileLine* fl, const string& name, AstNodeExpr* exprp, + VDirection::en direction) + : ASTGEN_SUPER_ModportVarRef(fl) + , m_name{name} + , m_direction{direction} { + this->exprp(exprp); + }; ASTGEN_MEMBERS_AstModportVarRef; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 1ce93ff9f..a3c62b46d 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2529,7 +2529,13 @@ class LinkDotIfaceVisitor final : public VNVisitor { void visit(AstModportVarRef* nodep) override { // IfaceVisitor:: UINFO(5, " fiv: " << nodep); iterateChildren(nodep); - VSymEnt* const symp = m_curSymp->findIdFallback(nodep->name()); + VSymEnt* symp = nullptr; + if (nodep->exprp()) { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported: Modport expressions (IEEE 1800-2023 25.5.4)"); + } else { + symp = m_curSymp->findIdFallback(nodep->name()); + } if (!symp) { nodep->v3error("Modport item not found: " << nodep->prettyNameQ()); } else if (AstVar* const varp = VN_CAST(symp->nodep(), Var)) { diff --git a/src/verilog.y b/src/verilog.y index 4231b47fe..078ddd850 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1679,8 +1679,7 @@ modportPortsDeclList: // We track the type as with the V2k series of defines, then create as each ID is seen. modportPortsDecl: // // IEEE: modport_simple_ports_declaration - port_direction modportSimplePortOrTFPort { $$ = new AstModportVarRef{$2, *$2, GRAMMARP->m_varIO}; - GRAMMARP->m_modportImpExpActive = false;} + port_direction { GRAMMARP->m_modportImpExpActive = false; } modportSimplePortOrTFPort { $$ = $3; } // // IEEE: modport_clocking_declaration | yCLOCKING idAny/*clocking_identifier*/ { $$ = new AstModportClockingRef{$1, *$2}; } // // IEEE: yIMPORT modport_tf_port @@ -1700,19 +1699,20 @@ modportPortsDecl: { $$ = nullptr; BBUNSUP($1, "Unsupported: Modport export with prototype"); DEL($2); } // Continuations of above after a comma. // // IEEE: modport_simple_ports_declaration - | modportSimplePortOrTFPort { $$ = GRAMMARP->m_modportImpExpActive ? + | modportSimplePortOrTFPort { $$ = $1; } + ; + +modportSimplePortOrTFPort:// IEEE: modport_simple_port or modport_tf_port, depending what keyword was earlier + idAny { $$ = GRAMMARP->m_modportImpExpActive ? static_cast( new AstModportFTaskRef{ $1, *$1, GRAMMARP->m_modportImpExpLastIsExport} ) : static_cast( new AstModportVarRef{ $1, *$1, GRAMMARP->m_varIO} ); } - ; - -modportSimplePortOrTFPort:// IEEE: modport_simple_port or modport_tf_port, depending what keyword was earlier - idAny { $$ = $1; } - | '.' idAny '(' ')' { $$ = $2; BBUNSUP($1, "Unsupported: Modport dotted port name"); } - | '.' idAny '(' expr ')' { $$ = $2; BBUNSUP($1, "Unsupported: Modport dotted port name"); } + | '.' idAny '(' ')' { $$ = new AstModportVarRef{$2, *$2, GRAMMARP->m_varIO}; + BBUNSUP($4, "Unsupported: Modport empty expression"); } + | '.' idAny '(' expr ')' { $$ = new AstModportVarRef{$2, *$2, $4, GRAMMARP->m_varIO}; } ; //************************************************ diff --git a/test_regress/t/t_dist_warn_coverage.py b/test_regress/t/t_dist_warn_coverage.py index 35a6bb608..8d35ab603 100755 --- a/test_regress/t/t_dist_warn_coverage.py +++ b/test_regress/t/t_dist_warn_coverage.py @@ -68,7 +68,7 @@ for s in [ 'Unsupported: 4-state numbers in this context', 'Unsupported: Bind with instance list', 'Unsupported: Concatenation to form ', - 'Unsupported: Modport dotted port name', + 'Unsupported: Modport empty expression', 'Unsupported: Modport export with prototype', 'Unsupported: Modport import with prototype', 'Unsupported: Only one PSL clock allowed per assertion', diff --git a/test_regress/t/t_interface_modport_expr.out b/test_regress/t/t_interface_modport_expr.out new file mode 100644 index 000000000..277926b00 --- /dev/null +++ b/test_regress/t/t_interface_modport_expr.out @@ -0,0 +1,39 @@ +%Error-UNSUPPORTED: t/t_interface_modport_expr.v:15:22: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) + 15 | modport mp1(input .a(sig_a), output .b(sig_b)); + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: t/t_interface_modport_expr.v:15:22: Modport item not found: 'a' + 15 | modport mp1(input .a(sig_a), output .b(sig_b)); + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error-UNSUPPORTED: t/t_interface_modport_expr.v:15:40: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) + 15 | modport mp1(input .a(sig_a), output .b(sig_b)); + | ^ +%Error: t/t_interface_modport_expr.v:15:40: Modport item not found: 'b' + 15 | modport mp1(input .a(sig_a), output .b(sig_b)); + | ^ +%Error-UNSUPPORTED: t/t_interface_modport_expr.v:16:22: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) + 16 | modport mp2(input .a(sig_c), output .b(sig_d)); + | ^ +%Error: t/t_interface_modport_expr.v:16:22: Modport item not found: 'a' + 16 | modport mp2(input .a(sig_c), output .b(sig_d)); + | ^ +%Error-UNSUPPORTED: t/t_interface_modport_expr.v:16:40: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) + 16 | modport mp2(input .a(sig_c), output .b(sig_d)); + | ^ +%Error: t/t_interface_modport_expr.v:16:40: Modport item not found: 'b' + 16 | modport mp2(input .a(sig_c), output .b(sig_d)); + | ^ +%Error: t/t_interface_modport_expr.v:28:18: Can't find definition of 'a' in dotted variable/method: 'i.a' + 28 | assign i.b = i.a; + | ^ +%Error: t/t_interface_modport_expr.v:28:12: Can't find definition of 'b' in dotted variable/method: 'i.b' + 28 | assign i.b = i.a; + | ^ +%Error: t/t_interface_modport_expr.v:22:18: Can't find definition of 'a' in dotted variable/method: 'i.a' + 22 | assign i.b = i.a; + | ^ +%Error: t/t_interface_modport_expr.v:22:12: Can't find definition of 'b' in dotted variable/method: 'i.b' + 22 | assign i.b = i.a; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_interface_modport_expr.py b/test_regress/t/t_interface_modport_expr.py new file mode 100755 index 000000000..3fada1a58 --- /dev/null +++ b/test_regress/t/t_interface_modport_expr.py @@ -0,0 +1,20 @@ +#!/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(fails=test.vlt_all, expect_filename=test.golden_filename, + verilator_flags2=["--binary"]) + +if not test.vlt_all: + test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_modport_expr.v b/test_regress/t/t_interface_modport_expr.v new file mode 100644 index 000000000..112403ef7 --- /dev/null +++ b/test_regress/t/t_interface_modport_expr.v @@ -0,0 +1,46 @@ +// 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 + +// verilog_format: off +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +interface my_if; + logic sig_a, sig_b, sig_c, sig_d; + + modport mp1(input .a(sig_a), output .b(sig_b)); + modport mp2(input .a(sig_c), output .b(sig_d)); +endinterface + +module mod1 ( + my_if.mp1 i +); + assign i.b = i.a; +endmodule + +module mod2 ( + my_if.mp2 i +); + assign i.b = i.a; +endmodule + +module top (); + my_if myIf (); + assign myIf.sig_a = 1'b1, myIf.sig_c = 1'b1; + + mod1 mod1Instance (myIf); + mod2 mod2Instance (myIf); + + initial begin + #1; + `checkh(myIf.sig_a, myIf.sig_b); + `checkh(myIf.sig_c, myIf.sig_d); + #1; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_modport_expr_partsel.out b/test_regress/t/t_interface_modport_expr_partsel.out new file mode 100644 index 000000000..deb4ee46c --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_partsel.out @@ -0,0 +1,39 @@ +%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:16:22: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) + 16 | modport mp1(input .in(a[7:0]), output .out(b)); + | ^~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: t/t_interface_modport_expr_partsel.v:16:22: Modport item not found: 'in' + 16 | modport mp1(input .in(a[7:0]), output .out(b)); + | ^~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:16:42: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) + 16 | modport mp1(input .in(a[7:0]), output .out(b)); + | ^~~ +%Error: t/t_interface_modport_expr_partsel.v:16:42: Modport item not found: 'out' + 16 | modport mp1(input .in(a[7:0]), output .out(b)); + | ^~~ +%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:17:22: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) + 17 | modport mp2(input .in(a[15:8]), output .out(c)); + | ^~ +%Error: t/t_interface_modport_expr_partsel.v:17:22: Modport item not found: 'in' + 17 | modport mp2(input .in(a[15:8]), output .out(c)); + | ^~ +%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:17:43: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) + 17 | modport mp2(input .in(a[15:8]), output .out(c)); + | ^~~ +%Error: t/t_interface_modport_expr_partsel.v:17:43: Modport item not found: 'out' + 17 | modport mp2(input .in(a[15:8]), output .out(c)); + | ^~~ +%Error: t/t_interface_modport_expr_partsel.v:29:21: Can't find definition of 'in' in dotted variable/method: 'i.in' + 29 | assign i.out = ~i.in; + | ^~ +%Error: t/t_interface_modport_expr_partsel.v:29:12: Can't find definition of 'out' in dotted variable/method: 'i.out' + 29 | assign i.out = ~i.in; + | ^~~ +%Error: t/t_interface_modport_expr_partsel.v:23:20: Can't find definition of 'in' in dotted variable/method: 'i.in' + 23 | assign i.out = i.in; + | ^~ +%Error: t/t_interface_modport_expr_partsel.v:23:12: Can't find definition of 'out' in dotted variable/method: 'i.out' + 23 | assign i.out = i.in; + | ^~~ +%Error: Exiting due to diff --git a/test_regress/t/t_interface_modport_expr_partsel.py b/test_regress/t/t_interface_modport_expr_partsel.py new file mode 100755 index 000000000..3fada1a58 --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_partsel.py @@ -0,0 +1,20 @@ +#!/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(fails=test.vlt_all, expect_filename=test.golden_filename, + verilator_flags2=["--binary"]) + +if not test.vlt_all: + test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_modport_expr_partsel.v b/test_regress/t/t_interface_modport_expr_partsel.v new file mode 100644 index 000000000..e18b2bb43 --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_partsel.v @@ -0,0 +1,47 @@ +// 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 + +// verilog_format: off +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +interface my_if; + logic [15:0] a; + logic [7:0] b, c; + + modport mp1(input .in(a[7:0]), output .out(b)); + modport mp2(input .in(a[15:8]), output .out(c)); +endinterface + +module mod1 ( + my_if.mp1 i +); + assign i.out = i.in; +endmodule + +module mod2 ( + my_if.mp2 i +); + assign i.out = ~i.in; +endmodule + +module top (); + my_if myIf (); + assign myIf.a = 16'habcd; + + mod1 mod1Instance (myIf); + mod2 mod2Instance (myIf); + + initial begin + #1; + `checkh(myIf.b, myIf.a[7:0]); + `checkh(myIf.c, ~myIf.a[15:8]); + #1; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_json_only_tag.out b/test_regress/t/t_json_only_tag.out index d532a722d..8a8395be3 100644 --- a/test_regress/t/t_json_only_tag.out +++ b/test_regress/t/t_json_only_tag.out @@ -53,7 +53,7 @@ {"type":"VAR","name":"value","addr":"(X)","loc":"d,8:12,8:17","dtypep":"(W)","origName":"value","isSc":false,"isPrimaryIO":false,"isPrimaryClock":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"isStdRandomizeArg":false,"lifetime":"VSTATICI","varType":"VAR","dtypeName":"integer","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"MODPORT","name":"out_modport","addr":"(MB)","loc":"d,9:12,9:23", "varsp": [ - {"type":"MODPORTVARREF","name":"value","addr":"(NB)","loc":"d,9:32,9:37","direction":"OUTPUT","varp":"(X)"} + {"type":"MODPORTVARREF","name":"value","addr":"(NB)","loc":"d,9:32,9:37","direction":"OUTPUT","varp":"(X)","exprp": []} ]} ]} ],"filesp": [],