Merge pull request #5623 from YosysHQ/nella/opt-dff-rewrite

opt_dff restructure.
This commit is contained in:
nella 2026-01-28 14:41:40 +01:00 committed by GitHub
commit 8f6c4d40e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 931 additions and 651 deletions

102
kernel/pattern.h Normal file
View File

@ -0,0 +1,102 @@
#ifndef OPT_DFF_COMP_H
#define OPT_DFF_COMP_H
#include "kernel/rtlil.h"
#include <map>
#include <optional>
YOSYS_NAMESPACE_BEGIN
/**
* Pattern matching utilities for control signal analysis.
*
* A pattern_t maps control signals to required values, representing a
* product term (conjunction): {A=1, B=0} means "A AND !B".
*
* A patterns_t is a set of patterns representing a sum-of-products:
* {{A=1, B=0}, {A=0, C=1}} means "(A AND !B) OR (!A AND C)".
*
* Used for analyzing MUX tree control paths in DFF optimization.
*/
// Pattern matching for clock enable
// A pattern maps control signals to their required values for a MUX path
typedef std::map<RTLIL::SigBit, bool> pattern_t; // Set of control signals that must ALL match required vals
typedef std::set<pattern_t> patterns_t; // Alternative patterns (OR)
typedef std::pair<RTLIL::SigBit, bool> ctrl_t; // Control signal
typedef std::set<ctrl_t> ctrls_t; // Set of control signals that must ALL be active
/**
* Find if two patterns differ in exactly one variable.
* Example: {A=1,B=1} vs {A=1,B=0} returns B, allows simplification: (A&B) | (A&!B) => A
*/
inline std::optional<RTLIL::SigBit> find_complementary_pattern_var(
const pattern_t& left,
const pattern_t& right
) {
std::optional<RTLIL::SigBit> ret;
for (const auto &pt : left) {
// Left requires signal that right doesn't constrain - incompatible domains
if (right.count(pt.first) == 0)
return std::nullopt;
// Signal has same required value in both - not the complement variable
if (right.at(pt.first) == pt.second)
continue;
// Already found one differing signal, now found another - not simplifiable
if (ret)
return std::nullopt;
// First differing signal - candidate complement variable
ret = pt.first;
}
return ret;
}
/**
* Simplify a sum-of-products by merging complementary patterns: (A&B) | (A&!B) => A,
* and removing redundant patterns: A | (A&B) => A
*/
inline void simplify_patterns(patterns_t& patterns) {
auto new_patterns = patterns;
// Merge complementary patterns
bool optimized;
do {
optimized = false;
for (auto i = patterns.begin(); i != patterns.end(); i++) {
for (auto j = std::next(i, 1); j != patterns.end(); j++) {
const auto& left = (GetSize(*j) <= GetSize(*i)) ? *j : *i;
auto right = (GetSize(*i) < GetSize(*j)) ? *j : *i;
const auto complementary_var = find_complementary_pattern_var(left, right);
if (complementary_var && new_patterns.count(right)) {
new_patterns.erase(right);
right.erase(complementary_var.value());
new_patterns.insert(right);
optimized = true;
}
}
}
patterns = new_patterns;
} while(optimized);
// Remove redundant patterns
for (auto i = patterns.begin(); i != patterns.end(); ++i) {
for (auto j = std::next(i, 1); j != patterns.end(); ++j) {
const auto& left = (GetSize(*j) <= GetSize(*i)) ? *j : *i;
const auto& right = (GetSize(*i) < GetSize(*j)) ? *j : *i;
bool redundant = true;
for (const auto& pt : left)
if (right.count(pt.first) == 0 || right.at(pt.first) != pt.second)
redundant = false;
if (redundant)
new_patterns.erase(right);
}
}
patterns = std::move(new_patterns);
}
YOSYS_NAMESPACE_END
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,179 @@
#include <gtest/gtest.h>
#include "kernel/pattern.h"
YOSYS_NAMESPACE_BEGIN
class FindComplementaryPatternVarTest : public ::testing::Test {
protected:
RTLIL::Design *design;
RTLIL::Module *module;
RTLIL::Wire *wire_a;
RTLIL::Wire *wire_b;
RTLIL::Wire *wire_c;
RTLIL::Wire *bus;
void SetUp() override {
design = new RTLIL::Design;
module = design->addModule(ID(test_module));
wire_a = module->addWire(ID(a));
wire_b = module->addWire(ID(b));
wire_c = module->addWire(ID(c));
bus = module->addWire(ID(bus), 4);
}
void TearDown() override {
delete design;
}
RTLIL::SigBit bit(RTLIL::Wire *w, int offset = 0) {
return RTLIL::SigBit(w, offset);
}
};
TEST_F(FindComplementaryPatternVarTest, EmptyPatterns) {
pattern_t left, right;
auto result = find_complementary_pattern_var(left, right);
EXPECT_FALSE(result.has_value());
}
TEST_F(FindComplementaryPatternVarTest, IdenticalSingleVar) {
pattern_t left, right;
left[bit(wire_a)] = true;
right[bit(wire_a)] = true;
auto result = find_complementary_pattern_var(left, right);
EXPECT_FALSE(result.has_value());
}
TEST_F(FindComplementaryPatternVarTest, ComplementarySingleVar) {
pattern_t left, right;
left[bit(wire_a)] = true;
right[bit(wire_a)] = false;
auto result = find_complementary_pattern_var(left, right);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result.value(), bit(wire_a));
}
TEST_F(FindComplementaryPatternVarTest, MissingKeyInRight) {
pattern_t left, right;
left[bit(wire_a)] = true;
left[bit(wire_b)] = false;
right[bit(wire_a)] = true;
auto result = find_complementary_pattern_var(left, right);
EXPECT_FALSE(result.has_value());
}
TEST_F(FindComplementaryPatternVarTest, TwoVarsOneComplementary) {
pattern_t left, right;
left[bit(wire_a)] = true;
left[bit(wire_b)] = false;
right[bit(wire_a)] = true;
right[bit(wire_b)] = true;
auto result = find_complementary_pattern_var(left, right);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result.value(), bit(wire_b));
}
TEST_F(FindComplementaryPatternVarTest, TwoVarsBothComplementary) {
pattern_t left, right;
left[bit(wire_a)] = true;
left[bit(wire_b)] = false;
right[bit(wire_a)] = false;
right[bit(wire_b)] = true;
auto result = find_complementary_pattern_var(left, right);
EXPECT_FALSE(result.has_value());
}
TEST_F(FindComplementaryPatternVarTest, LeftSubsetOfRight) {
pattern_t left, right;
left[bit(wire_a)] = true;
left[bit(wire_b)] = false;
right[bit(wire_a)] = true;
right[bit(wire_b)] = true;
right[bit(wire_c)] = false;
auto result = find_complementary_pattern_var(left, right);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result.value(), bit(wire_b));
}
TEST_F(FindComplementaryPatternVarTest, ThreeVarsAllSame) {
pattern_t left, right;
left[bit(wire_a)] = true;
left[bit(wire_b)] = false;
left[bit(wire_c)] = true;
right[bit(wire_a)] = true;
right[bit(wire_b)] = false;
right[bit(wire_c)] = true;
auto result = find_complementary_pattern_var(left, right);
EXPECT_FALSE(result.has_value());
}
TEST_F(FindComplementaryPatternVarTest, PracticalPatternSimplification) {
pattern_t pattern1, pattern2;
pattern1[bit(bus, 0)] = true;
pattern1[bit(bus, 1)] = true;
pattern2[bit(bus, 0)] = true;
pattern2[bit(bus, 1)] = false;
auto result = find_complementary_pattern_var(pattern1, pattern2);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result.value(), bit(bus, 1));
// Swapped args
auto result2 = find_complementary_pattern_var(pattern2, pattern1);
ASSERT_TRUE(result2.has_value());
EXPECT_EQ(result2.value(), bit(bus, 1));
}
TEST_F(FindComplementaryPatternVarTest, MuxTreeClockEnableDetection) {
pattern_t feedback_path1, feedback_path2;
feedback_path1[bit(wire_a)] = true;
feedback_path1[bit(wire_b)] = true;
feedback_path2[bit(wire_a)] = true;
feedback_path2[bit(wire_b)] = false;
auto comp = find_complementary_pattern_var(feedback_path1, feedback_path2);
ASSERT_TRUE(comp.has_value());
EXPECT_EQ(comp.value(), bit(wire_b));
pattern_t simplified = feedback_path1;
simplified.erase(comp.value());
EXPECT_EQ(simplified.size(), 1);
EXPECT_TRUE(simplified.count(bit(wire_a)));
EXPECT_TRUE(simplified[bit(wire_a)]);
}
TEST_F(FindComplementaryPatternVarTest, AsymmetricPatterns) {
pattern_t left, right;
left[bit(wire_a)] = true;
right[bit(wire_a)] = false;
right[bit(wire_b)] = true;
right[bit(wire_c)] = false;
auto result = find_complementary_pattern_var(left, right);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result.value(), bit(wire_a));
}
TEST_F(FindComplementaryPatternVarTest, WireOffsetDistinction) {
pattern_t left, right;
left[bit(bus, 0)] = true;
left[bit(bus, 1)] = false;
right[bit(bus, 0)] = true;
right[bit(bus, 1)] = true;
right[bit(bus, 2)] = false;
auto result = find_complementary_pattern_var(left, right);
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result.value(), bit(bus, 1));
}
YOSYS_NAMESPACE_END