diff --git a/src/V3Ast.h b/src/V3Ast.h index 56329157e..5bd63e855 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -899,6 +899,8 @@ public: SUPPLY1, WIRE, WREAL, + TRIAND, + TRIOR, TRIWIRE, TRI0, TRI1, @@ -919,20 +921,24 @@ public: constexpr operator en() const { return m_e; } const char* ascii() const { static const char* const names[] - = {"?", "GPARAM", "LPARAM", "GENVAR", "VAR", "SUPPLY0", "SUPPLY1", - "WIRE", "WREAL", "TRIWIRE", "TRI0", "TRI1", "PORT", "BLOCKTEMP", - "MODULETEMP", "STMTTEMP", "XTEMP", "IFACEREF", "MEMBER"}; + = {"?", "GPARAM", "LPARAM", "GENVAR", "VAR", "SUPPLY0", "SUPPLY1", + "WIRE", "WREAL", "TRIAND", "TRIOR", "TRIWIRE", "TRI0", "TRI1", + "PORT", "BLOCKTEMP", "MODULETEMP", "STMTTEMP", "XTEMP", "IFACEREF", "MEMBER"}; return names[m_e]; } bool isParam() const { return m_e == GPARAM || m_e == LPARAM; } bool isSignal() const { return (m_e == WIRE || m_e == WREAL || m_e == TRIWIRE || m_e == TRI0 || m_e == TRI1 - || m_e == PORT || m_e == SUPPLY0 || m_e == SUPPLY1 || m_e == VAR); + || m_e == PORT || m_e == SUPPLY0 || m_e == SUPPLY1 || m_e == VAR || m_e == TRIOR + || m_e == TRIAND); } bool isNet() const { return (m_e == WIRE || m_e == TRIWIRE || m_e == TRI0 || m_e == TRI1 || m_e == SUPPLY0 - || m_e == SUPPLY1); + || m_e == SUPPLY1 || m_e == TRIOR || m_e == TRIAND); } + bool isWor() const { return (m_e == TRIOR); } + bool isWand() const { return (m_e == TRIAND); } + bool isWiredNet() const { return (m_e == TRIOR || m_e == TRIAND); } bool isContAssignable() const { // In Verilog, always ok in SystemVerilog return (m_e == SUPPLY0 || m_e == SUPPLY1 || m_e == WIRE || m_e == WREAL || m_e == TRIWIRE || m_e == TRI0 || m_e == TRI1 || m_e == PORT || m_e == BLOCKTEMP @@ -963,6 +969,8 @@ public: /* SUPPLY1: */ "SUPPLY1", /* WIRE: */ "WIRE", /* WREAL: */ "WIRE", + /* TRIAND: */ "TRIAND", + /* TRIOR: */ "TRIOR", /* TRIWIRE: */ "TRI", /* TRI0: */ "TRI0", /* TRI1: */ "TRI1", diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 8600c7512..59a5cd027 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -2073,6 +2073,9 @@ public: bool isInternal() const { return m_isInternal; } bool isSignal() const { return varType().isSignal(); } bool isNet() const { return varType().isNet(); } + bool isWor() const { return varType().isWor(); } + bool isWand() const { return varType().isWand(); } + bool isWiredNet() const { return varType().isWiredNet(); } bool isTemp() const { return varType().isTemp(); } bool isToggleCoverable() const { return ((isIO() || isSignal()) diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index adb6fdc90..b30be761d 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -914,6 +914,51 @@ class TristateVisitor final : public TristateBaseVisitor { || (strength1 <= strength && assignmentOfValueOnAllBits(assignp, 1)); } + AstNodeExpr* newMergeExpr(AstNodeExpr* const lhsp, AstNodeExpr* const rhsp, FileLine* const fl, + bool isWor) { + AstNodeExpr* expr = nullptr; + if (isWor) + expr = new AstOr{fl, lhsp, rhsp}; + else + expr = new AstAnd{fl, lhsp, rhsp}; + return expr; + } + + void mergeWiredNetsAssignments() { + // Support for WOR/TRIOR/WAND/TRIAND, by merging the Assignments for the + // same Net (merge by or for WOR/TIOR and merge by and for WAND/TRIAND). + for (auto& varpAssigns : m_assigns) { + Assigns& assigns = varpAssigns.second; + if (assigns.size() > 1) { + AstVar* varp = varpAssigns.first; + if (varp->isWiredNet()) { + auto it = assigns.begin(); + AstAssignW* const assignWp0 = *it; + FileLine* const fl = assignWp0->fileline(); + AstNodeExpr* wExp = nullptr; + while (++it != assigns.end()) { + AstAssignW* assignWpi = *it; + if (!wExp) { + wExp = newMergeExpr(assignWp0->rhsp()->cloneTreePure(false), + assignWpi->rhsp()->cloneTreePure(false), fl, + varp->isWor()); + } else { + wExp = newMergeExpr(wExp, assignWpi->rhsp()->cloneTreePure(false), fl, + varp->isWor()); + } + VL_DO_DANGLING((assignWpi->unlinkFrBack()->deleteTree()), assignWpi); + } + AstVarRef* const wVarRef = new AstVarRef{fl, varp, VAccess::WRITE}; + AstAssignW* const wAssignp = new AstAssignW{fl, wVarRef, wExp}; + assignWp0->replaceWith(wAssignp); + VL_DO_DANGLING(pushDeletep(assignWp0), assignWp0); + assigns.clear(); + assigns.push_back(wAssignp); + } + } + } + } + void removeNotStrongerAssignments(Assigns& assigns, AstAssignW* strongestp, uint8_t greatestKnownStrength) { // Weaker assignments are these assignments that can't change the final value of the net. @@ -1776,6 +1821,8 @@ class TristateVisitor final : public TristateBaseVisitor { iterateChildren(nodep); m_graphing = false; } + // Merge the assignments for very Wired net LHS : wor, trior, wand and triand + mergeWiredNetsAssignments(); // Remove all assignments not stronger than the strongest uniform constant removeAssignmentsNotStrongerThanUniformConstant(); // Use graph to find tristate signals diff --git a/src/verilog.y b/src/verilog.y index 73a78d791..9fbf16bde 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1994,12 +1994,12 @@ net_type: // ==IEEE: net_type | yTRI { VARDECL(TRIWIRE); } | yTRI0 { VARDECL(TRI0); } | yTRI1 { VARDECL(TRI1); } - | yTRIAND { VARDECL(WIRE); BBUNSUP($1, "Unsupported: triand"); } - | yTRIOR { VARDECL(WIRE); BBUNSUP($1, "Unsupported: trior"); } + | yTRIAND { VARDECL(TRIAND); } + | yTRIOR { VARDECL(TRIOR); } | yTRIREG { VARDECL(WIRE); BBUNSUP($1, "Unsupported: trireg"); } - | yWAND { VARDECL(WIRE); BBUNSUP($1, "Unsupported: wand"); } + | yWAND { VARDECL(TRIAND); } | yWIRE { VARDECL(WIRE); } - | yWOR { VARDECL(WIRE); BBUNSUP($1, "Unsupported: wor"); } + | yWOR { VARDECL(TRIOR); } // // VAMS - somewhat hackish | yWREAL { VARDECL(WREAL); } ; diff --git a/test_regress/t/t_wire_triand.out b/test_regress/t/t_wire_triand.out deleted file mode 100644 index c64acb3a9..000000000 --- a/test_regress/t/t_wire_triand.out +++ /dev/null @@ -1,8 +0,0 @@ -%Error-UNSUPPORTED: t/t_wire_triand.v:11:4: Unsupported: triand - 11 | triand ta; - | ^~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_wire_triand.v:12:4: Unsupported: trior - 12 | trior to; - | ^~~~~ -%Error: Exiting due to diff --git a/test_regress/t/t_wire_triand.v b/test_regress/t/t_wire_triand.v deleted file mode 100644 index dcd94f310..000000000 --- a/test_regress/t/t_wire_triand.v +++ /dev/null @@ -1,14 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module for SystemVerilog 'alias' -// -// Simple bi-directional alias test. -// -// This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2024 by Wilson Snyder. -// SPDX-License-Identifier: CC0-1.0 - -module t (/*AUTOARG*/); - - triand ta; - trior to; - -endmodule diff --git a/test_regress/t/t_wire_triand.py b/test_regress/t/t_wired_net_test.py similarity index 81% rename from test_regress/t/t_wire_triand.py rename to test_regress/t/t_wired_net_test.py index 30c3d4f77..d4f986441 100755 --- a/test_regress/t/t_wire_triand.py +++ b/test_regress/t/t_wired_net_test.py @@ -9,8 +9,10 @@ import vltest_bootstrap -test.scenarios('linter') +test.scenarios('simulator') -test.lint(fails=test.vlt_all, expect_filename=test.golden_filename) +test.compile() + +test.execute() test.passes() diff --git a/test_regress/t/t_wired_net_test.v b/test_regress/t/t_wired_net_test.v new file mode 100755 index 000000000..741c558a1 --- /dev/null +++ b/test_regress/t/t_wired_net_test.v @@ -0,0 +1,71 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define checkb(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='b%x exp='b%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); + +module t( +clk +/*AUTOARG*/); + input clk; + wor [3:0] ptrior1; + trior [3:0] ptrior2; + wand [3:0] ptriand1; + triand [3:0] ptriand2; + wire [3:0] z1; + wire [3:0] z2; + wire [3:0] tri_z1; + wire [3:0] tri_z2; + logic [3:0] x; + logic [3:0] y; + logic [3:0] tri_x; + logic [3:0] tri_y; + logic [3:0] tri_x_dat; + logic [3:0] tri_y_dat; + logic [3:0] tri_x_en; + logic [3:0] tri_y_en; + assign ptrior1 = x & y; + assign ptrior1 = x + y; + assign ptrior2 = tri_x & tri_y; + assign ptrior2 = tri_x + tri_y; + assign ptriand1 = x & y; + assign ptriand1 = x + y; + assign ptriand2 = tri_x & tri_y; + assign ptriand2 = tri_x + tri_y; + assign z1 = (x & y) | (x + y); + assign z2 = (x & y) & (x + y); + assign tri_z1 = (tri_x & tri_y) | (tri_x + tri_y); + assign tri_z2 = (tri_x & tri_y) & (tri_x + tri_y); + integer cyc = 0; + integer xz_index = 0; + integer xz_num = 0; + integer i; + assign tri_x[0] = tri_x_en[0] ? tri_x_dat[0] : 1'bz; + assign tri_x[1] = tri_x_en[1] ? tri_x_dat[1] : 1'bz; + assign tri_x[2] = tri_x_en[2] ? tri_x_dat[2] : 1'bz; + assign tri_x[3] = tri_x_en[3] ? tri_x_dat[3] : 1'bz; + assign tri_y[0] = tri_y_en[0] ? tri_y_dat[0] : 1'bz; + assign tri_y[1] = tri_y_en[1] ? tri_y_dat[1] : 1'bz; + assign tri_y[2] = tri_y_en[2] ? tri_y_dat[2] : 1'bz; + assign tri_y[3] = tri_y_en[3] ? tri_y_dat[3] : 1'bz; + always @ (posedge clk) begin + cyc <= cyc + 1; + x = {$random}[3:0]; + y = {$random}[3:0]; + tri_x_dat = {$random}[3:0]; + tri_y_dat = {$random}[3:0]; + tri_x_en = {$random}[3:0]; + tri_y_en = {$random}[3:0]; + `checkb(ptrior1, z1); + `checkb(ptrior2, tri_z1); + `checkb(ptriand1, z2); + `checkb(ptriand2, tri_z2); + if (cyc == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule