mirror of https://github.com/YosysHQ/yosys.git
Merge from upstream
This commit is contained in:
parent
e8d27892f0
commit
16087ae931
2
Makefile
2
Makefile
|
|
@ -177,7 +177,7 @@ ifeq ($(OS), Haiku)
|
||||||
CXXFLAGS += -D_DEFAULT_SOURCE
|
CXXFLAGS += -D_DEFAULT_SOURCE
|
||||||
endif
|
endif
|
||||||
|
|
||||||
YOSYS_VER := 0.61+80
|
YOSYS_VER := 0.61+97
|
||||||
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
|
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
|
||||||
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2)
|
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2)
|
||||||
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'.' -f3)
|
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'.' -f3)
|
||||||
|
|
|
||||||
2
abc
2
abc
|
|
@ -1 +1 @@
|
||||||
Subproject commit 01ad37aada7566964219c993818af75234f93ce0
|
Subproject commit 9dcae29da366ba9b7b518a8426545811be1ea61e
|
||||||
|
|
@ -78,7 +78,7 @@ ContentListing* ContentListing::open_option(const string &text,
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_LINE_LEN 80
|
#define MAX_LINE_LEN 80
|
||||||
void log_pass_str(const std::string &pass_str, std::string indent_str, bool leading_newline=false) {
|
void log_body_str(const std::string &pass_str, std::string indent_str, bool leading_newline=false, bool is_formatted=false) {
|
||||||
if (pass_str.empty())
|
if (pass_str.empty())
|
||||||
return;
|
return;
|
||||||
std::istringstream iss(pass_str);
|
std::istringstream iss(pass_str);
|
||||||
|
|
@ -86,26 +86,30 @@ void log_pass_str(const std::string &pass_str, std::string indent_str, bool lead
|
||||||
log("\n");
|
log("\n");
|
||||||
for (std::string line; std::getline(iss, line);) {
|
for (std::string line; std::getline(iss, line);) {
|
||||||
log("%s", indent_str);
|
log("%s", indent_str);
|
||||||
auto curr_len = indent_str.length();
|
if (is_formatted) {
|
||||||
std::istringstream lss(line);
|
log("%s", line);
|
||||||
for (std::string word; std::getline(lss, word, ' ');) {
|
} else {
|
||||||
while (word[0] == '`' && word.back() == '`')
|
auto curr_len = indent_str.length();
|
||||||
word = word.substr(1, word.length()-2);
|
std::istringstream lss(line);
|
||||||
if (curr_len + word.length() >= MAX_LINE_LEN-1) {
|
for (std::string word; std::getline(lss, word, ' ');) {
|
||||||
curr_len = 0;
|
while (word[0] == '`' && word.back() == '`')
|
||||||
log("\n%s", indent_str);
|
word = word.substr(1, word.length()-2);
|
||||||
}
|
if (curr_len + word.length() >= MAX_LINE_LEN-1) {
|
||||||
if (word.length()) {
|
curr_len = 0;
|
||||||
log("%s ", word);
|
log("\n%s", indent_str);
|
||||||
curr_len += word.length() + 1;
|
}
|
||||||
|
if (word.length()) {
|
||||||
|
log("%s ", word);
|
||||||
|
curr_len += word.length() + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log("\n");
|
log("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void log_pass_str(const std::string &pass_str, int indent=0, bool leading_newline=false) {
|
void log_body(const ContentListing &content, int indent=0, bool leading_newline=false) {
|
||||||
std::string indent_str(indent*4, ' ');
|
std::string indent_str(indent*4, ' ');
|
||||||
log_pass_str(pass_str, indent_str, leading_newline);
|
log_body_str(content.body, indent_str, leading_newline, content.type.compare("code") == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
PrettyHelp *current_help = nullptr;
|
PrettyHelp *current_help = nullptr;
|
||||||
|
|
@ -134,16 +138,16 @@ void PrettyHelp::log_help() const
|
||||||
{
|
{
|
||||||
for (auto &content : _root_listing) {
|
for (auto &content : _root_listing) {
|
||||||
if (content.type.compare("usage") == 0) {
|
if (content.type.compare("usage") == 0) {
|
||||||
log_pass_str(content.body, 1, true);
|
log_body(content, 1, true);
|
||||||
log("\n");
|
log("\n");
|
||||||
} else if (content.type.compare("option") == 0) {
|
} else if (content.type.compare("option") == 0) {
|
||||||
log_pass_str(content.body, 1);
|
log_body(content, 1);
|
||||||
for (auto text : content) {
|
for (auto text : content) {
|
||||||
log_pass_str(text.body, 2);
|
log_body(text, 2);
|
||||||
log("\n");
|
log("\n");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log_pass_str(content.body, 0);
|
log_body(content, 0);
|
||||||
log("\n");
|
log("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -30,26 +30,31 @@ PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
struct LinuxPerf : public Pass {
|
struct LinuxPerf : public Pass {
|
||||||
LinuxPerf() : Pass("linux_perf", "turn linux perf recording off or on") { }
|
LinuxPerf() : Pass("linux_perf", "turn linux perf recording off or on") {
|
||||||
void help() override
|
internal();
|
||||||
|
}
|
||||||
|
bool formatted_help() override
|
||||||
{
|
{
|
||||||
log("This pass turns Linux 'perf' profiling on or off, when it has been configured to use control FIFOs.\n");
|
auto *help = PrettyHelp::get_current();
|
||||||
log("\n");
|
|
||||||
log("Example shell command line:\n");
|
auto content_root = help->get_root();
|
||||||
log("mkfifo /tmp/perf.fifo /tmp/perf-ack.fifo\n");
|
|
||||||
log("YOSYS_PERF_CTL=/tmp/perf.fifo YOSYS_PERF_ACK=/tmp/perf-ack.fifo \\\n");
|
content_root->usage("linux_perf [on|off]");
|
||||||
log(" perf record --latency --delay=-1 \\\n");
|
|
||||||
log(" --control=fifo:/tmp/perf.fifo,/tmp/perf-ack.fifo --call-graph=dwarf ./yosys -dt -p \\\n");
|
content_root->paragraph(
|
||||||
log(" \"read_rtlil design.rtlil; linux_perf on; opt_clean; linux_perf off\"\n");
|
"This pass turns Linux 'perf' profiling on or off, when it has been configured to use control FIFOs."
|
||||||
log("\n");
|
"YOSYS_PERF_CTL and YOSYS_PERF_ACK must point to Linux perf control FIFOs."
|
||||||
log(" linux_perf on\n");
|
);
|
||||||
log("\n");
|
content_root->paragraph("Example shell command line:");
|
||||||
log("Start perf recording. YOSYS_PERF_CTL and YOSYS_PERF_ACK must point to Linux perf control FIFOs.\n");
|
content_root->codeblock(
|
||||||
log("\n");
|
"mkfifo /tmp/perf.fifo /tmp/perf-ack.fifo\n"
|
||||||
log(" linux_perf off\n");
|
"YOSYS_PERF_CTL=/tmp/perf.fifo YOSYS_PERF_ACK=/tmp/perf-ack.fifo \\\n"
|
||||||
log("\n");
|
" perf record --latency --delay=-1 \\\n"
|
||||||
log("Stop perf recording.\n");
|
" --control=fifo:/tmp/perf.fifo,/tmp/perf-ack.fifo --call-graph=dwarf ./yosys \\\n"
|
||||||
log("\n");
|
" -dt -p \"read_rtlil design.rtlil; linux_perf on; opt_clean; linux_perf off\"\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *) override
|
void execute(std::vector<std::string> args, RTLIL::Design *) override
|
||||||
{
|
{
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||||
Loading…
Reference in New Issue