Remove assignments with strengths weaker than strongest non-tristate RHS (#3629)

This commit is contained in:
Ryszard Rozak 2022-09-19 10:54:20 +02:00 committed by GitHub
parent fc4ffd454e
commit fe2a1e1749
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 189 additions and 65 deletions

View File

@ -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<TristateVertex*>(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<TristateVertex*>(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<TristateVertex*>(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

View File

@ -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

View File

@ -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;

View File

@ -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