diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 5a04e135e..f88908f62 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -57,11 +57,16 @@ // // // Another thing done in this phase is signal strength handling. -// Currently they are only supported in assignments and only in case when the strongest assignment -// has constant with all bits equal on the RHS. It is the case when they can be statically -// resolved. +// Currently they are only supported in assignments and gates parsed as assignments (see verilog.y) +// and only in case when the strongest assignment has RHS marked as non-tristate. It is the case +// when they can be statically resolved. +// If the RHS is equal to z, that assignment has to be skipped. Since the value may be not known at +// verilation time, cases with tristates on RHS can't be handled statically. // -// Static resolution is done in the following way: +// Static resolution is split into 2 parts. +// First part can be done before tristate propagation. It is about removing assignments that are +// weaker or equally strong as the strongest assignment with constant on RHS that has all bits +// the same (equal to 0 or 1). It is done in the following way: // - The assignment of value 0 (size may be greater than 1), that has greatest strength (the // one corresponding to 0) of all other assignments of 0, has to be found. // - The same is done for value '1 and strength corresponding to value 1. @@ -72,16 +77,19 @@ // both strengths non-greater that the one was found, because they are weaker no matter what is on // RHS. // -// All assignments that are stronger than the one with strongest constant are left as they are. +// Second part of static resolution is done after tristate propagation. +// At that moment it is known that some expressions can't be equal to z. The exact value is +// unknown (except the ones with constants that were handled before), so weaker of both strengths +// has to be taken into account. All weaker assignments can be safely removed. It is done in +// similar way to the first part: +// - The assignment with non-tristate RHS with the greatest weaker strength has to be found. +// - Then all not stronger assignments can be removed. +// +// All assignments that are stronger than the strongest with non-tristate RHS are left as they are. // // There is a possible problem with equally strong assignments, because multiple assignments with // the same strength, but different values should result in x value, but these values are // unsupported. -// -// Singal strength can also be used in simple logic gates parsed as assignments (see verilog.y), -// but these gates are then either removed (if they are weaker than the strongest constant) or -// handled as the gates witout signal strengths are handled now. In other words, gate with greater -// strength won't properly overwrite weaker driver. //************************************************************************* #include "config_build.h" @@ -269,6 +277,18 @@ public: void associate(AstNode* fromp, AstNode* top) { new V3GraphEdge(&m_graph, makeVertex(fromp), makeVertex(top), 1); } + void deleteVerticesFromSubtreeRecurse(AstNode* nodep) { + if (!nodep) return; + // Skip vars, because they may be connected to more than one varref + if (!VN_IS(nodep, Var)) { + TristateVertex* const vertexp = reinterpret_cast(nodep->user5p()); + if (vertexp) vertexp->unlinkDelete(&m_graph); + } + deleteVerticesFromSubtreeRecurse(nodep->op1p()); + deleteVerticesFromSubtreeRecurse(nodep->op2p()); + deleteVerticesFromSubtreeRecurse(nodep->op3p()); + deleteVerticesFromSubtreeRecurse(nodep->op4p()); + } void setTristate(AstNode* nodep) { makeVertex(nodep)->isTristate(true); } bool isTristate(AstNode* nodep) { const TristateVertex* const vertexp = reinterpret_cast(nodep->user5p()); @@ -715,64 +735,93 @@ class TristateVisitor final : public TristateBaseVisitor { return assignmentOfValueOnAllBits(*maxIt, value) ? *maxIt : nullptr; } - void removeWeakerAssignments(Assigns& assigns) { + bool isAssignmentNotStrongerThanStrength(AstAssignW* assignp, uint8_t strength) { + // If the value of the RHS is known and has all bits equal, only strength corresponding to + // its value is taken into account. In opposite case, both strengths are compared. + const uint8_t strength0 = getStrength(assignp, 0); + const uint8_t strength1 = getStrength(assignp, 1); + return (strength0 <= strength && strength1 <= strength) + || (strength0 <= strength && assignmentOfValueOnAllBits(assignp, 0)) + || (strength1 <= strength && assignmentOfValueOnAllBits(assignp, 1)); + } + + void removeNotStrongerAssignments(Assigns& assigns, AstAssignW* strongestp, + uint8_t greatestKnownStrength) { // Weaker assignments are these assignments that can't change the final value of the net. - // If the value of the RHS is known, only strength corresponding to its value is taken into - // account. Assignments of constants that have bits both 0 and 1 are skipped here, because - // it would involve handling parts of bits separately. - - // First, the strongest assignment, that has value on the RHS consisting of only 1 or only - // 0, has to be found. - AstAssignW* const strongest0p = getStrongestAssignmentOfValue(assigns, 0); - AstAssignW* const strongest1p = getStrongestAssignmentOfValue(assigns, 1); - AstAssignW* strongestp = nullptr; - uint8_t greatestKnownStrength = 0; - const auto getIfStrongest = [&](AstAssignW* const strongestCandidatep, bool value) { - if (!strongestCandidatep) return; - uint8_t strength = getStrength(strongestCandidatep, value); - if (strength >= greatestKnownStrength) { - greatestKnownStrength = strength; - strongestp = strongestCandidatep; + // They can be safely removed. Assignments of the same strength are also removed, because + // duplicates aren't needed. One problem is with 2 assignments of different values and + // equal strengths. It should result in assignment of x value, but these values aren't + // supported now. + auto removedIt = std::remove_if(assigns.begin(), assigns.end(), [&](AstAssignW* assignp) { + if (assignp == strongestp) return false; + if (isAssignmentNotStrongerThanStrength(assignp, greatestKnownStrength)) { + // Vertices corresponding to nodes from removed assignment's subtree have to be + // removed. + m_tgraph.deleteVerticesFromSubtreeRecurse(assignp); + VL_DO_DANGLING(pushDeletep(assignp->unlinkFrBack()), assignp); + return true; } - }; - getIfStrongest(strongest0p, 0); - getIfStrongest(strongest1p, 1); + return false; + }); + assigns.erase(removedIt, assigns.end()); + } - if (strongestp) { - // Then all weaker assignments can be safely removed. - // Assignments of the same strength are also removed, because duplicates aren't needed. - // One problem is with 2 assignments of different values and equal strengths. It should - // result in assignment of x value, but these values aren't supported now. - auto removedIt - = std::remove_if(assigns.begin(), assigns.end(), [&](AstAssignW* assignp) { - if (assignp == strongestp) return false; - const uint8_t strength0 = getStrength(assignp, 0); - const uint8_t strength1 = getStrength(assignp, 1); - const bool toRemove = (strength0 <= greatestKnownStrength - && strength1 <= greatestKnownStrength) - || (strength0 <= greatestKnownStrength - && assignmentOfValueOnAllBits(assignp, 0)) - || (strength1 <= greatestKnownStrength - && assignmentOfValueOnAllBits(assignp, 1)); - if (toRemove) { - // Don't propagate tristate if its assignment is removed. - TristateVertex* const vertexp - = reinterpret_cast(assignp->rhsp()->user5p()); - if (vertexp) vertexp->isTristate(false); - VL_DO_DANGLING(pushDeletep(assignp->unlinkFrBack()), assignp); - return true; - } - return false; - }); - assigns.erase(removedIt, assigns.end()); + void removeAssignmentsNotStrongerThanUniformConstant() { + // If a stronger assignment of a constant with all bits equal to the same + // value (0 or 1), is found, all weaker assignments can be safely removed. + for (auto& varpAssigns : m_assigns) { + Assigns& assigns = varpAssigns.second; + if (assigns.size() > 1) { + AstAssignW* const strongest0p = getStrongestAssignmentOfValue(assigns, 0); + AstAssignW* const strongest1p = getStrongestAssignmentOfValue(assigns, 1); + AstAssignW* strongestp = nullptr; + uint8_t greatestKnownStrength = 0; + const auto getIfStrongest + = [&](AstAssignW* const strongestCandidatep, bool value) { + if (!strongestCandidatep) return; + uint8_t strength = getStrength(strongestCandidatep, value); + if (strength >= greatestKnownStrength) { + greatestKnownStrength = strength; + strongestp = strongestCandidatep; + } + }; + getIfStrongest(strongest0p, 0); + getIfStrongest(strongest1p, 1); + + if (strongestp) { + removeNotStrongerAssignments(assigns, strongestp, greatestKnownStrength); + } + } } } - void resolveMultipleNetAssignments() { + void removeAssignmentsNotStrongerThanNonTristate() { + // Similar function as removeAssignmentsNotStrongerThanUniformConstant, but here the + // assignments that have strength not stronger than the strongest assignment with + // non-tristate RHS are removed. Strengths are compared according to their smaller values, + // because the values of RHSs are unknown. (Assignments not stronger than strongest + // constant are already removed.) for (auto& varpAssigns : m_assigns) { - if (varpAssigns.second.size() > 1) { - // first the static resolution is tried - removeWeakerAssignments(varpAssigns.second); + Assigns& assigns = varpAssigns.second; + if (assigns.size() > 1) { + auto maxIt = std::max_element( + assigns.begin(), assigns.end(), [&](AstAssignW* ap, AstAssignW* bp) { + if (m_tgraph.isTristate(ap)) return !m_tgraph.isTristate(bp); + if (m_tgraph.isTristate(bp)) return false; + const uint8_t minStrengthA + = std::min(getStrength(ap, 0), getStrength(ap, 1)); + const uint8_t minStrengthB + = std::min(getStrength(bp, 0), getStrength(bp, 1)); + return minStrengthA < minStrengthB; + }); + // If RHSs of all assignments are tristate, 1st element is returned, so it is + // needed to check if it is non-tristate. + AstAssignW* const strongestp = m_tgraph.isTristate(*maxIt) ? nullptr : *maxIt; + if (strongestp) { + uint8_t greatestKnownStrength + = std::min(getStrength(strongestp, 0), getStrength(strongestp, 1)); + removeNotStrongerAssignments(assigns, strongestp, greatestKnownStrength); + } } } } @@ -1534,10 +1583,14 @@ class TristateVisitor final : public TristateBaseVisitor { iterateChildren(nodep); m_graphing = false; } - // resolve multiple net assignments and signal strengths - resolveMultipleNetAssignments(); + // Remove all assignments not stronger than the strongest uniform constant + removeAssignmentsNotStrongerThanUniformConstant(); // Use graph to find tristate signals m_tgraph.graphWalk(nodep); + + // Remove all assignments not stronger than the strongest non-tristate RHS + removeAssignmentsNotStrongerThanNonTristate(); + // Build the LHS drivers map for this module iterateChildren(nodep); // Insert new logic for all tristates diff --git a/test_regress/t/t_strength_assignments.pl b/test_regress/t/t_strength_assignments_constants.pl similarity index 100% rename from test_regress/t/t_strength_assignments.pl rename to test_regress/t/t_strength_assignments_constants.pl diff --git a/test_regress/t/t_strength_assignments.v b/test_regress/t/t_strength_assignments_constants.v similarity index 100% rename from test_regress/t/t_strength_assignments.v rename to test_regress/t/t_strength_assignments_constants.v diff --git a/test_regress/t/t_weak_nor_strong_assign.pl b/test_regress/t/t_strength_strongest_constant.pl similarity index 100% rename from test_regress/t/t_weak_nor_strong_assign.pl rename to test_regress/t/t_strength_strongest_constant.pl diff --git a/test_regress/t/t_weak_nor_strong_assign.v b/test_regress/t/t_strength_strongest_constant.v similarity index 50% rename from test_regress/t/t_weak_nor_strong_assign.v rename to test_regress/t/t_strength_strongest_constant.v index a3811ca71..d8fec7098 100644 --- a/test_regress/t/t_weak_nor_strong_assign.v +++ b/test_regress/t/t_strength_strongest_constant.v @@ -4,15 +4,30 @@ // any use, without warranty, 2022 by Antmicro Ltd. // SPDX-License-Identifier: CC0-1.0 -module t (/*AUTOARG*/); +module t (clk1, clk2); + input wire clk1; + input wire clk2; + wire a; nor (pull0, weak1) n1(a, 0, 0); assign (strong0, weak1) a = 0; + wire [1:0] b; + assign (weak0, supply1) b = '1; + assign b = clk1 ? '0 : 'z; + + wire c = 1; + assign (weak0, pull1) c = clk1 & clk2; + always begin - if (!a) begin + if (!a && b === '1 && c) begin $write("*-* All Finished *-*\n"); $finish; end + else begin + $write("Error: a = %b, b = %b, c = %b ", a, b, c); + $write("expected: a = 0, b = 11, c = 1\n"); + $stop; + end end endmodule diff --git a/test_regress/t/t_strength_strongest_non_tristate.pl b/test_regress/t/t_strength_strongest_non_tristate.pl new file mode 100755 index 000000000..f5e338520 --- /dev/null +++ b/test_regress/t/t_strength_strongest_non_tristate.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 by Antmicro Ltd. 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_strength_strongest_non_tristate.v b/test_regress/t/t_strength_strongest_non_tristate.v new file mode 100644 index 000000000..5c3c64c86 --- /dev/null +++ b/test_regress/t/t_strength_strongest_non_tristate.v @@ -0,0 +1,35 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t (clk1, clk2); + input wire clk1; + input wire clk2; + + wire (weak0, weak1) a = 0; + assign (strong0, supply1) a = clk1; + assign (pull0, pull1) a = 1; + + wire b; + xor (strong0, strong1) (b, clk1, clk2); + and (weak0, pull1) (b, clk1, clk2); + + wire [7:0] c; + assign (supply0, strong1) c = clk1 ? '1 : '0; + assign (weak0, supply1) c = '0; + assign (weak0, pull1) c = 'z; + + always begin + if (a === clk1 && b === clk1 ^ clk2 && c[0] === clk1) begin + $write("*-* All Finished *-*\n"); + $finish; + end + else begin + $write("Error: a = %b, b = %b, c[0] = %b, ", a, b, c[0]); + $write("expected: a = %b, b = %b, c[0] = %b\n", clk1, clk1 ^ clk2, clk1); + $stop; + end + end +endmodule