From fb864e91ee7ba47cc52a38024c353acec156b78a Mon Sep 17 00:00:00 2001 From: Natalia Date: Wed, 14 Jan 2026 17:35:45 -0800 Subject: [PATCH 01/21] Add Design::run_pass() API for programmatic pass execution This commit adds a new run_pass() method to the RTLIL::Design class, providing a convenient API for executing Yosys passes programmatically. This is particularly useful for PyYosys users who want to run passes on a design object without needing to manually construct Pass::call() invocations. The method wraps Pass::call() with appropriate logging to maintain consistency with command-line pass execution. Example usage (from Python): design = ys.Design() # ... build or load design ... design.run_pass("hierarchy") design.run_pass("proc") design.run_pass("opt") Changes: - kernel/rtlil.h: Add run_pass() method declaration - kernel/rtlil.cc: Implement run_pass() method - tests/unit/kernel/test_design_run_pass.cc: Add unit tests --- kernel/rtlil.cc | 7 +++ kernel/rtlil.h | 3 ++ tests/unit/kernel/test_design_run_pass.cc | 59 +++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 tests/unit/kernel/test_design_run_pass.cc diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 0103cabfb..357ac2c5a 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1610,6 +1610,13 @@ std::vector RTLIL::Design::selected_modules(RTLIL::SelectPartial return result; } +void RTLIL::Design::run_pass(std::string command) +{ + log("\n-- Running command `%s' --\n", command.c_str()); + Pass::call(this, command); + log_flush(); +} + RTLIL::Module::Module() { static unsigned int hashidx_count = 123456789; diff --git a/kernel/rtlil.h b/kernel/rtlil.h index fe280c965..532aa20b4 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -2031,6 +2031,9 @@ struct RTLIL::Design // returns all selected unboxed whole modules, warning the user if any // partially selected or boxed modules have been ignored std::vector selected_unboxed_whole_modules_warn() const { return selected_modules(SELECT_WHOLE_WARN, SB_UNBOXED_WARN); } + + void run_pass(std::string command); + static std::map *get_all_designs(void); std::string to_rtlil_str(bool only_selected = true) const; diff --git a/tests/unit/kernel/test_design_run_pass.cc b/tests/unit/kernel/test_design_run_pass.cc new file mode 100644 index 000000000..0553f4eb2 --- /dev/null +++ b/tests/unit/kernel/test_design_run_pass.cc @@ -0,0 +1,59 @@ +#include +#include "kernel/rtlil.h" +#include "kernel/register.h" + +YOSYS_NAMESPACE_BEGIN + +class DesignRunPassTest : public testing::Test { +protected: + DesignRunPassTest() { + if (log_files.empty()) log_files.emplace_back(stdout); + } + virtual void SetUp() override { + IdString::ensure_prepopulated(); + } +}; + +TEST_F(DesignRunPassTest, RunPassExecutesSuccessfully) +{ + // Create a design with a simple module + RTLIL::Design *design = new RTLIL::Design; + RTLIL::Module *module = new RTLIL::Module; + module->name = RTLIL::IdString("\\test_module"); + design->add(module); + + // Add a simple wire to the module + RTLIL::Wire *wire = module->addWire(RTLIL::IdString("\\test_wire"), 1); + wire->port_input = true; + wire->port_id = 1; + module->fixup_ports(); + + // Call run_pass with a simple pass + // We use "check" which is a simple pass that just validates the design + ASSERT_NO_THROW(design->run_pass("check")); + + // Verify the design still exists and has the module + EXPECT_EQ(design->modules().size(), 1); + EXPECT_NE(design->module(RTLIL::IdString("\\test_module")), nullptr); + + delete design; +} + +TEST_F(DesignRunPassTest, RunPassWithHierarchy) +{ + // Create a design with a simple module + RTLIL::Design *design = new RTLIL::Design; + RTLIL::Module *module = new RTLIL::Module; + module->name = RTLIL::IdString("\\top"); + design->add(module); + + // Call run_pass with hierarchy pass + ASSERT_NO_THROW(design->run_pass("hierarchy")); + + // Verify the design still has the module + EXPECT_EQ(design->modules().size(), 1); + + delete design; +} + +YOSYS_NAMESPACE_END From cf511628b0dc3ec3cc3d372cab3f74f0208f79cc Mon Sep 17 00:00:00 2001 From: Natalia Date: Sun, 18 Jan 2026 02:11:09 -0800 Subject: [PATCH 02/21] modify generator for pyosys/wrappers.cc instead of headers --- kernel/rtlil.cc | 7 --- kernel/rtlil.h | 2 - pyosys/generator.py | 10 ++++ tests/pyosys/test_design_run_pass.py | 12 +++++ tests/unit/kernel/test_design_run_pass.cc | 59 ----------------------- 5 files changed, 22 insertions(+), 68 deletions(-) create mode 100644 tests/pyosys/test_design_run_pass.py delete mode 100644 tests/unit/kernel/test_design_run_pass.cc diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 357ac2c5a..0103cabfb 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1610,13 +1610,6 @@ std::vector RTLIL::Design::selected_modules(RTLIL::SelectPartial return result; } -void RTLIL::Design::run_pass(std::string command) -{ - log("\n-- Running command `%s' --\n", command.c_str()); - Pass::call(this, command); - log_flush(); -} - RTLIL::Module::Module() { static unsigned int hashidx_count = 123456789; diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 532aa20b4..fea53081e 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -2032,8 +2032,6 @@ struct RTLIL::Design // partially selected or boxed modules have been ignored std::vector selected_unboxed_whole_modules_warn() const { return selected_modules(SELECT_WHOLE_WARN, SB_UNBOXED_WARN); } - void run_pass(std::string command); - static std::map *get_all_designs(void); std::string to_rtlil_str(bool only_selected = true) const; diff --git a/pyosys/generator.py b/pyosys/generator.py index 7d4293abd..0dda98015 100644 --- a/pyosys/generator.py +++ b/pyosys/generator.py @@ -701,6 +701,16 @@ class PyosysWrapperGenerator(object): self.process_class_members(metadata, metadata, cls, basename) + if basename == "Design": + print( + '\t\t\t.def("run_pass", [](Design &s, std::vector cmd) { Pass::call(cmd, &s); })', + file=self.f, + ) + print( + '\t\t\t.def("run_pass", [](Design &s, std::string cmd) { Pass::call(cmd, &s); })', + file=self.f, + ) + if expr := metadata.string_expr: print( f'\t\t.def("__str__", [](const {basename} &s) {{ return {expr}; }})', diff --git a/tests/pyosys/test_design_run_pass.py b/tests/pyosys/test_design_run_pass.py new file mode 100644 index 000000000..c9656fd7a --- /dev/null +++ b/tests/pyosys/test_design_run_pass.py @@ -0,0 +1,12 @@ +from pathlib import Path + +from pyosys import libyosys as ys + +__file_dir__ = Path(__file__).absolute().parent + +design = ys.Design() +design.run_pass( + ["read_verilog", str(__file_dir__.parent / "simple" / "fiedler-cooley.v")] +) +design.run_pass("prep") +design.run_pass(["opt", "-full"]) diff --git a/tests/unit/kernel/test_design_run_pass.cc b/tests/unit/kernel/test_design_run_pass.cc deleted file mode 100644 index 0553f4eb2..000000000 --- a/tests/unit/kernel/test_design_run_pass.cc +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include "kernel/rtlil.h" -#include "kernel/register.h" - -YOSYS_NAMESPACE_BEGIN - -class DesignRunPassTest : public testing::Test { -protected: - DesignRunPassTest() { - if (log_files.empty()) log_files.emplace_back(stdout); - } - virtual void SetUp() override { - IdString::ensure_prepopulated(); - } -}; - -TEST_F(DesignRunPassTest, RunPassExecutesSuccessfully) -{ - // Create a design with a simple module - RTLIL::Design *design = new RTLIL::Design; - RTLIL::Module *module = new RTLIL::Module; - module->name = RTLIL::IdString("\\test_module"); - design->add(module); - - // Add a simple wire to the module - RTLIL::Wire *wire = module->addWire(RTLIL::IdString("\\test_wire"), 1); - wire->port_input = true; - wire->port_id = 1; - module->fixup_ports(); - - // Call run_pass with a simple pass - // We use "check" which is a simple pass that just validates the design - ASSERT_NO_THROW(design->run_pass("check")); - - // Verify the design still exists and has the module - EXPECT_EQ(design->modules().size(), 1); - EXPECT_NE(design->module(RTLIL::IdString("\\test_module")), nullptr); - - delete design; -} - -TEST_F(DesignRunPassTest, RunPassWithHierarchy) -{ - // Create a design with a simple module - RTLIL::Design *design = new RTLIL::Design; - RTLIL::Module *module = new RTLIL::Module; - module->name = RTLIL::IdString("\\top"); - design->add(module); - - // Call run_pass with hierarchy pass - ASSERT_NO_THROW(design->run_pass("hierarchy")); - - // Verify the design still has the module - EXPECT_EQ(design->modules().size(), 1); - - delete design; -} - -YOSYS_NAMESPACE_END From b43c96b03da9a3d1a4ea358c6bc0920f5723f3e0 Mon Sep 17 00:00:00 2001 From: Natalia Date: Sun, 18 Jan 2026 02:24:36 -0800 Subject: [PATCH 03/21] fix pyosys Design.run_pass binding to use Pass::call signature --- pyosys/generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyosys/generator.py b/pyosys/generator.py index 0dda98015..f1d429724 100644 --- a/pyosys/generator.py +++ b/pyosys/generator.py @@ -703,11 +703,11 @@ class PyosysWrapperGenerator(object): if basename == "Design": print( - '\t\t\t.def("run_pass", [](Design &s, std::vector cmd) { Pass::call(cmd, &s); })', + '\t\t\t.def("run_pass", [](Design &s, std::vector cmd) { Pass::call(&s, cmd); })', file=self.f, ) print( - '\t\t\t.def("run_pass", [](Design &s, std::string cmd) { Pass::call(cmd, &s); })', + '\t\t\t.def("run_pass", [](Design &s, std::string cmd) { Pass::call(&s, cmd); })', file=self.f, ) From 7439d2489e4e5bfedd987d0a7a306955cf451bf7 Mon Sep 17 00:00:00 2001 From: Natalia Date: Thu, 29 Jan 2026 02:20:50 -0800 Subject: [PATCH 04/21] add assertion to run_pass test --- tests/pyosys/test_design_run_pass.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/pyosys/test_design_run_pass.py b/tests/pyosys/test_design_run_pass.py index c9656fd7a..59316f269 100644 --- a/tests/pyosys/test_design_run_pass.py +++ b/tests/pyosys/test_design_run_pass.py @@ -3,10 +3,11 @@ from pathlib import Path from pyosys import libyosys as ys __file_dir__ = Path(__file__).absolute().parent +src = __file_dir__.parent / "simple" / "fiedler-cooley.v" design = ys.Design() -design.run_pass( - ["read_verilog", str(__file_dir__.parent / "simple" / "fiedler-cooley.v")] -) -design.run_pass("prep") -design.run_pass(["opt", "-full"]) +design.run_pass(["read_verilog", str(src)]) +design.run_pass("hierarchy -top up3down5") +design.run_pass(["proc"]) +design.run_pass("opt -full") +design.run_pass("select -assert-mod-count 1 up3down5") From 61b1c3c75a56343dc494bfc514321615dad351d5 Mon Sep 17 00:00:00 2001 From: Natalia Date: Thu, 29 Jan 2026 02:42:23 -0800 Subject: [PATCH 05/21] use run_pass in ecp5 add/sub test --- tests/pyosys/test_design_run_pass.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/tests/pyosys/test_design_run_pass.py b/tests/pyosys/test_design_run_pass.py index 59316f269..f0013577d 100644 --- a/tests/pyosys/test_design_run_pass.py +++ b/tests/pyosys/test_design_run_pass.py @@ -1,13 +1,20 @@ -from pathlib import Path - +from pathlib import Path from pyosys import libyosys as ys __file_dir__ = Path(__file__).absolute().parent -src = __file_dir__.parent / "simple" / "fiedler-cooley.v" +add_sub = __file_dir__.parent / "arch" / "common" / "add_sub.v" -design = ys.Design() -design.run_pass(["read_verilog", str(src)]) -design.run_pass("hierarchy -top up3down5") -design.run_pass(["proc"]) -design.run_pass("opt -full") -design.run_pass("select -assert-mod-count 1 up3down5") +base = ys.Design() +base.run_pass(["read_verilog", str(add_sub)]) +base.run_pass("hierarchy -top top") +base.run_pass(["proc"]) +base.run_pass("equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5") + +postopt = ys.Design() +postopt.run_pass("design -load postopt") +postopt.run_pass(["cd", "top"]) +postopt.run_pass("select -assert-min 25 t:LUT4") +postopt.run_pass("select -assert-max 26 t:LUT4") +postopt.run_pass(["select", "-assert-count", "10", "t:PFUMX"]) +postopt.run_pass(["select", "-assert-count", "6", "t:L6MUX21"]) +postopt.run_pass("select -assert-none t:LUT4 t:PFUMX t:L6MUX21 %% t:* %D") From 6af1b5b19c279c09319f3fa8c094df89c06e9f7e Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Thu, 29 Jan 2026 18:47:12 +0000 Subject: [PATCH 06/21] Don't treat ABC 'Error:' output as indicating a fatal error, since these messages aren't necessarily fatal --- passes/techmap/abc.cc | 58 +++++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index e73be611a..ae0f3e053 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -1127,9 +1127,34 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module handle_loops(assign_map, module); } +static bool is_abc_prompt(const std::string &line, std::string &rest) { + size_t pos = 0; + while (true) { + // The prompt may not start at the start of the line, because + // ABC can output progress and maybe other data that isn't + // newline-terminated. + size_t start = line.find("abc ", pos); + if (start == std::string::npos) + return false; + pos = start + 4; + + size_t digits = 0; + while (pos + digits < line.size() && line[pos + digits] >= '0' && line[pos + digits] <= '9') + ++digits; + if (digits < 2) + return false; + if (line.substr(pos + digits, 2) == "> ") { + rest = line.substr(pos + digits + 2); + return true; + } + } +} + bool read_until_abc_done(abc_output_filter &filt, int fd, DeferredLogs &logs) { std::string line; char buf[1024]; + bool seen_source_cmd = false; + bool seen_yosys_abc_done = false; while (true) { int ret = read(fd, buf, sizeof(buf) - 1); if (ret < 0) { @@ -1144,23 +1169,30 @@ bool read_until_abc_done(abc_output_filter &filt, int fd, DeferredLogs &logs) { char *end = buf + ret; while (start < end) { char *p = static_cast(memchr(start, '\n', end - start)); - if (p == nullptr) { - break; + char *upto = p == nullptr ? end : p + 1; + line.append(start, upto - start); + start = upto; + + std::string rest; + bool is_prompt = is_abc_prompt(line, rest); + if (is_prompt && seen_source_cmd) { + // This is the first prompt after we sourced the script. + // We are done here. + // We won't have seen a newline yet since ABC is waiting at the prompt. + if (!seen_yosys_abc_done) + logs.log_error("ABC script did not complete successfully\n"); + return seen_yosys_abc_done; } - line.append(start, p + 1 - start); - if (line.substr(0, 14) == "YOSYS_ABC_DONE") { - // Ignore any leftover output, there should only be a prompt perhaps - return true; - } - // If ABC aborted the sourced script, it returns to the prompt and will - // never print YOSYS_ABC_DONE. Treat this as a failed run, not a hang. - if (line.substr(0, 7) == "Error: ") { - logs.log_error("ABC: %s", line.c_str()); - return false; + if (line.empty() || line[line.size() - 1] != '\n') { + // No newline yet, wait for more text + continue; } filt.next_line(line); + if (is_prompt && rest.substr(0, 7) == "source ") + seen_source_cmd = true; + if (line.substr(0, 14) == "YOSYS_ABC_DONE") + seen_yosys_abc_done = true; line.clear(); - start = p + 1; } line.append(start, end - start); } From 9c56c93632f0aca1a7ded76582ce41dea08d906a Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Thu, 29 Jan 2026 18:47:42 +0000 Subject: [PATCH 07/21] Add missing newlines to some 'log_error's --- passes/techmap/abc.cc | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index ae0f3e053..e9d02b85c 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -188,10 +188,10 @@ struct AbcProcess int status; int ret = waitpid(pid, &status, 0); if (ret != pid) { - log_error("waitpid(%d) failed", pid); + log_error("waitpid(%d) failed\n", pid); } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - log_error("ABC failed with status %X", status); + log_error("ABC failed with status %X\n", status); } if (from_child_pipe >= 0) close(from_child_pipe); @@ -203,12 +203,12 @@ std::optional spawn_abc(const char* abc_exe, DeferredLogs &logs) { // fork()s. int to_child_pipe[2]; if (pipe2(to_child_pipe, O_CLOEXEC) != 0) { - logs.log_error("pipe failed"); + logs.log_error("pipe failed\n"); return std::nullopt; } int from_child_pipe[2]; if (pipe2(from_child_pipe, O_CLOEXEC) != 0) { - logs.log_error("pipe failed"); + logs.log_error("pipe failed\n"); return std::nullopt; } @@ -221,39 +221,39 @@ std::optional spawn_abc(const char* abc_exe, DeferredLogs &logs) { posix_spawn_file_actions_t file_actions; if (posix_spawn_file_actions_init(&file_actions) != 0) { - logs.log_error("posix_spawn_file_actions_init failed"); + logs.log_error("posix_spawn_file_actions_init failed\n"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, to_child_pipe[1]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed"); + logs.log_error("posix_spawn_file_actions_addclose failed\n"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, from_child_pipe[0]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed"); + logs.log_error("posix_spawn_file_actions_addclose failed\n"); return std::nullopt; } if (posix_spawn_file_actions_adddup2(&file_actions, to_child_pipe[0], STDIN_FILENO) != 0) { - logs.log_error("posix_spawn_file_actions_adddup2 failed"); + logs.log_error("posix_spawn_file_actions_adddup2 failed\n"); return std::nullopt; } if (posix_spawn_file_actions_adddup2(&file_actions, from_child_pipe[1], STDOUT_FILENO) != 0) { - logs.log_error("posix_spawn_file_actions_adddup2 failed"); + logs.log_error("posix_spawn_file_actions_adddup2 failed\n"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, to_child_pipe[0]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed"); + logs.log_error("posix_spawn_file_actions_addclose failed\n"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, from_child_pipe[1]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed"); + logs.log_error("posix_spawn_file_actions_addclose failed\n"); return std::nullopt; } char arg1[] = "-s"; char* argv[] = { strdup(abc_exe), arg1, nullptr }; if (0 != posix_spawnp(&result.pid, abc_exe, &file_actions, nullptr, argv, environ)) { - logs.log_error("posix_spawnp %s failed (errno=%s)", abc_exe, strerror(errno)); + logs.log_error("posix_spawnp %s failed (errno=%s)\n", abc_exe, strerror(errno)); return std::nullopt; } free(argv[0]); @@ -1158,11 +1158,11 @@ bool read_until_abc_done(abc_output_filter &filt, int fd, DeferredLogs &logs) { while (true) { int ret = read(fd, buf, sizeof(buf) - 1); if (ret < 0) { - logs.log_error("Failed to read from ABC, errno=%d", errno); + logs.log_error("Failed to read from ABC, errno=%d\n", errno); return false; } if (ret == 0) { - logs.log_error("ABC exited prematurely"); + logs.log_error("ABC exited prematurely\n"); return false; } char *start = buf; From b88d6588bc23fff3dd30a112b2646be288505c68 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 2 Feb 2026 11:25:57 +0100 Subject: [PATCH 08/21] Update ABC as per 2026-02-02 --- abc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abc b/abc index 79010216c..734f64d5b 160000 --- a/abc +++ b/abc @@ -1 +1 @@ -Subproject commit 79010216cb87427dd7a0c8d38f156494221be006 +Subproject commit 734f64d5b907158dc4337ee82b3b74566d74ba08 From 224549fb88fd4d2301d643aa1fb60958d631b93c Mon Sep 17 00:00:00 2001 From: Sean Luchen Date: Mon, 2 Feb 2026 15:26:03 -0800 Subject: [PATCH 09/21] Guard vhdl_file::UNDEFINED behind VERIFIC_VHDL_SUPPORT. Signed-off-by: Sean Luchen --- frontends/verific/verific.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 9f13eee23..6a1c81aa4 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -3701,7 +3701,9 @@ struct VerificPass : public Pass { if (GetSize(args) > argidx && (args[argidx] == "-f" || args[argidx] == "-F")) { unsigned verilog_mode = veri_file::UNDEFINED; +#ifdef VERIFIC_VHDL_SUPPORT unsigned vhdl_mode = vhdl_file::UNDEFINED; +#endif bool is_formal = false; const char* filename = nullptr; From 153ddc0c84ce8de4896a7b11918199b8fc1022ac Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 00:33:37 +0000 Subject: [PATCH 10/21] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e4ebf9887..92a854819 100644 --- a/Makefile +++ b/Makefile @@ -161,7 +161,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.61+112 +YOSYS_VER := 0.61+129 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From fc11754557046827cecbb77c2c3c4f3484c52c98 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Tue, 3 Feb 2026 12:09:24 +0100 Subject: [PATCH 11/21] Release version 0.62 --- CHANGELOG | 20 +++++++++++++++++++- Makefile | 4 ++-- docs/source/conf.py | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 73c1606da..6e2bca32c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,8 +2,26 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.61 .. Yosys 0.62-dev +Yosys 0.61 .. Yosys 0.62 -------------------------- + * Various + - verific: Added "-sv2017" flag option to support System + Verilog 2017. + - verific: Added VHDL related flags to "-f" and "-F" and + support reading VHDL file from file lists. + - Updated cell libs with proper module declaration where + non standard (...) style was used. + + * New commands and options + - Added "-word" option to "lut2mux" pass to enable emitting + word level cells. + - Added experimental "opt_balance_tree" pass to convert + cascaded cells into tree of cells to improve timing. + - Added "-gatesi" option to "write_blif" pass to init gates + under gates_mode in BLIF format. + - Added "-on" and "-off" options to "debug" pass for + persistent debug logging. + - Added "linux_perf" pass to control performance recording. Yosys 0.60 .. Yosys 0.61 -------------------------- diff --git a/Makefile b/Makefile index 92a854819..28608f573 100644 --- a/Makefile +++ b/Makefile @@ -161,7 +161,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.61+129 +YOSYS_VER := 0.62 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) @@ -186,7 +186,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 5ae48ee.. | wc -l`/;" Makefile +# sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 5ae48ee.. | wc -l`/;" Makefile ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) diff --git a/docs/source/conf.py b/docs/source/conf.py index 34f8be029..a7da22d97 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,7 +6,7 @@ import os project = 'YosysHQ Yosys' author = 'YosysHQ GmbH' copyright ='2026 YosysHQ GmbH' -yosys_ver = "0.61" +yosys_ver = "0.62" # select HTML theme html_theme = 'furo-ys' From 6620d098d45eefa9380ab22b048ec9e90c11787a Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Tue, 3 Feb 2026 12:05:14 -0800 Subject: [PATCH 12/21] lower verbosity --- passes/sat/sim.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index c029cb2df..34c94695f 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -258,9 +258,13 @@ struct SimInstance if ((shared->fst) && !(shared->hide_internal && wire->name[0] == '$')) { fstHandle id = shared->fst->getHandle(scope + "." + RTLIL::unescape_id(wire->name)); if (id==0 && wire->name.isPublic()) { - log_warning("Unable to find wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(wire->name))); + if (shared->debug) { + log_warning("Unable to find wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(wire->name))); + } } else { - log("Found wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(wire->name))); + if (shared->debug) { + log("Found wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(wire->name))); + } } fst_handles[wire] = id; } From ce959ec1bb6c7ab9ac7e8273f8966a33b7fedb26 Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Tue, 3 Feb 2026 12:42:33 -0800 Subject: [PATCH 13/21] fixes --- passes/sat/sim.cc | 51 +++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 34c94695f..d7b1c323e 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -2508,16 +2508,11 @@ struct AnnotateActivity : public OutputWriter { } } - // Retrieve VCD timescale - std::string timescale = worker->timescale; - double real_timescale = 1e-12; // ps - if (timescale == "ns") - real_timescale = 1e-9; - if (timescale == "fs") - real_timescale = 1e-15; - - // TODO: remove all debug sections when dev is completed - bool debug = false; + // Retrieve timescale from converted VCD file + double real_timescale = worker->fst->getTimescale(); + if (worker->debug) { + log_debug("Timescale %e seconds extracted from converted VCD file", real_timescale); + } // Compute clock period, find the highest toggling signal and compute its average period SignalActivityDataMap::iterator itr = dataMap.find(clk); @@ -2531,31 +2526,32 @@ struct AnnotateActivity : public OutputWriter { std::stringstream ss; ss << std::setprecision(4) << real_timescale; worker->top->module->set_string_attribute("$TIMESCALE", ss.str()); - if (debug) { - std::cout << "Clock toggle count: " << clktoggleCounts[0] << "\n"; - std::cout << "Max time: " << max_time << "\n"; - std::cout << "Clock period: " << clk_period << "\n"; - std::cout << "Frequency: " << frequency << "\n"; + if (worker->debug) { + log_debug("Clock toggle count: %f", clktoggleCounts[0]); + log_debug("Max time: %d", max_time); + log_debug("Clock period: %f", clk_period); + log_debug("Frequency: %f", frequency); } double totalActivity = 0.0f; double totalDuty = 0.0f; + + // TODO make this debug code less messy and more readable. worker->top->write_output_header( - [debug](IdString name) { - if (debug) - std::cout << stringf("module %s\n", log_id(name)); + [&](IdString name) { + if (worker->debug) + log_debug("module %s", log_id(name)); }, - [debug]() { - if (debug) - std::cout << "endmodule\n"; + [&]() { + if (worker->debug) + log_debug("endmodule"); }, - [&use_signal, &dataMap, max_time, real_timescale, clk_period, debug, &totalActivity, &totalDuty] - (const char *name, int size, Wire *w, int id, bool) { + [&](const char *name, int size, Wire *w, int id, bool) { if (!use_signal.at(id) || (w == nullptr)) return; SignalActivityDataMap::const_iterator itr = dataMap.find(id); const std::vector &toggleCounts = itr->second.toggleCounts; const std::vector &highTimes = itr->second.highTimes; - if (debug) { + if (worker->debug) { std::string full_name = form_vcd_name(name, size, w); std::cout << full_name << " " << id << ":\n"; std::cout << " TC: "; @@ -2581,7 +2577,7 @@ struct AnnotateActivity : public OutputWriter { totalActivity += activity; activity_str += std::to_string(activity) + " "; } - if (debug) { + if (worker->debug) { std::cout << activity_str; std::cout << "\n"; std::cout << " DUTY: "; @@ -2593,9 +2589,8 @@ struct AnnotateActivity : public OutputWriter { totalDuty += duty; duty_str += std::to_string(duty) + " "; } - if (debug) { - std::cout << duty_str; - std::cout << "\n"; + if (worker->debug) { + log_debug(" DUTY: %s", duty_str.c_str()); } w->set_string_attribute("$ACKT", activity_str); w->set_string_attribute("$DUTY", duty_str); From bea2a7d47302c0dd3de1e70a0cfbcf8b81e4b882 Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Tue, 3 Feb 2026 14:40:33 -0800 Subject: [PATCH 14/21] add few debug --- passes/sat/sim.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index d7b1c323e..a0cfee542 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -2578,9 +2578,7 @@ struct AnnotateActivity : public OutputWriter { activity_str += std::to_string(activity) + " "; } if (worker->debug) { - std::cout << activity_str; - std::cout << "\n"; - std::cout << " DUTY: "; + log_debug(" ACKT: %s", activity_str.c_str()); } std::string duty_str; for (uint32_t i = 0; i < (uint32_t)size; i++) { From 643427d9c9532dc19e534f1a34b84eaf6a9a14fa Mon Sep 17 00:00:00 2001 From: tondapusili Date: Tue, 3 Feb 2026 17:09:03 -0800 Subject: [PATCH 15/21] Add negopt pass with comprehensive pattern matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces the negopt pass with pre/post optimization modes for handling negation patterns in arithmetic circuits. Pre-optimization patterns (expose for tree balancing): - manual2sub: (a + ~b) + 1 → a - b - sub2neg: a - b → a + (-b) - negexpand: -(a + b) → (-a) + (-b) [with output width fix] - negneg: -(-a) → a - negmux: -(s ? a : b) → s ? (-a) : (-b) Post-optimization patterns (cleanup/rebuild): - negrebuild: (-a) + (-b) → -(a + b) - muxneg: s ? (-a) : (-b) → -(s ? a : b) - neg2sub: a + (-b) → a - b All patterns use nusers() for fanout checking (standard Yosys style). Comprehensive test coverage with positive/negative cases and formal verification via equiv_opt. --- passes/silimate/Makefile.inc | 17 +++ passes/silimate/negopt.cc | 119 +++++++++++++++++ passes/silimate/peepopt_manual2sub.pmg | 171 +++++++++++++++++++++++++ passes/silimate/peepopt_muxneg.pmg | 59 +++++++++ passes/silimate/peepopt_neg2sub.pmg | 60 +++++++++ passes/silimate/peepopt_negexpand.pmg | 56 ++++++++ passes/silimate/peepopt_negmux.pmg | 52 ++++++++ passes/silimate/peepopt_negneg.pmg | 35 +++++ passes/silimate/peepopt_negrebuild.pmg | 63 +++++++++ passes/silimate/peepopt_sub2neg.pmg | 42 ++++++ tests/silimate/manual2sub.ys | 73 +++++++++++ tests/silimate/muxneg.ys | 44 +++++++ tests/silimate/neg2sub.ys | 41 ++++++ tests/silimate/negexpand.ys | 65 ++++++++++ tests/silimate/negmux.ys | 69 ++++++++++ tests/silimate/negneg.ys | 60 +++++++++ tests/silimate/negrebuild.ys | 19 +++ tests/silimate/sub2neg.ys | 39 ++++++ 18 files changed, 1084 insertions(+) create mode 100644 passes/silimate/negopt.cc create mode 100644 passes/silimate/peepopt_manual2sub.pmg create mode 100644 passes/silimate/peepopt_muxneg.pmg create mode 100644 passes/silimate/peepopt_neg2sub.pmg create mode 100644 passes/silimate/peepopt_negexpand.pmg create mode 100644 passes/silimate/peepopt_negmux.pmg create mode 100644 passes/silimate/peepopt_negneg.pmg create mode 100644 passes/silimate/peepopt_negrebuild.pmg create mode 100644 passes/silimate/peepopt_sub2neg.pmg create mode 100644 tests/silimate/manual2sub.ys create mode 100644 tests/silimate/muxneg.ys create mode 100644 tests/silimate/neg2sub.ys create mode 100644 tests/silimate/negexpand.ys create mode 100644 tests/silimate/negmux.ys create mode 100644 tests/silimate/negneg.ys create mode 100644 tests/silimate/negrebuild.ys create mode 100644 tests/silimate/sub2neg.ys diff --git a/passes/silimate/Makefile.inc b/passes/silimate/Makefile.inc index 6d40f16e8..98ddf5aa4 100644 --- a/passes/silimate/Makefile.inc +++ b/passes/silimate/Makefile.inc @@ -33,3 +33,20 @@ PEEPOPT_PATTERN += passes/silimate/peepopt_muxinvprop.pmg passes/silimate/peepopt_muxmode.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) $(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) + +OBJS += passes/silimate/negopt.o +GENFILES += passes/silimate/peepopt_negopt.h +passes/silimate/negopt.o: passes/silimate/peepopt_negopt.h +$(eval $(call add_extra_objs,passes/silimate/peepopt_negopt.h)) + +PEEPOPT_PATTERN = passes/silimate/peepopt_manual2sub.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_sub2neg.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_negexpand.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_negneg.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_negmux.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_negrebuild.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_muxneg.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_neg2sub.pmg + +passes/silimate/peepopt_negopt.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) + $(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) diff --git a/passes/silimate/negopt.cc b/passes/silimate/negopt.cc new file mode 100644 index 000000000..af5d68496 --- /dev/null +++ b/passes/silimate/negopt.cc @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2025 Silimate, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// +// Authored by Abhinav Tondapu of Silimate, Inc. +// + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool did_something; + +#include "passes/silimate/peepopt_negopt.h" + +struct NegoptPass : public Pass { + NegoptPass() : Pass("negopt", "optimize negation patterns in arithmetic") { } + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" negopt [options] [selection]\n"); + log("\n"); + log("This pass optimizes negation patterns in arithmetic expressions.\n"); + log("\n"); + log(" -pre\n"); + log(" Run pre-optimization transformations:\n" ); + log(" - manual2sub: a + ~b + 1 => a - b\n" ); + log(" - sub2neg: a - b => a + (-b)\n" ); + log(" - negexpand: -(a + b) => (-a) + (-b)\n"); + log(" - negneg: -(-a) => a\n" ); + log(" - negmux: -(s?a:b) => s?(-a):(-b)\n"); + log("\n"); + log(" -post\n"); + log(" Run post-optimization transformations:\n" ); + log(" - negrebuild: (-a)+(-b) => -(a + b)\n"); + log(" - muxneg: s?(-a):(-b) => -(s?a:b)\n"); + log(" - neg2sub: a + (-b) => a - b\n" ); + log("\n"); + log("When called without options, both -pre and -post are executed.\n"); + log("\n"); + } + + void execute(std::vector args, RTLIL::Design *design) override + { + bool run_pre = false; + bool run_post = false; + + log_header(design, "Executing NEGOPT pass (optimize negation patterns).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-pre") { + run_pre = true; + continue; + } + if (args[argidx] == "-post") { + run_post = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!run_pre && !run_post) { + run_pre = true; + run_post = true; + } + + for (auto module : design->selected_modules()) { + if (run_pre) { + did_something = true; + while (did_something) { + did_something = false; + peepopt_pm pm(module); + pm.setup(module->selected_cells()); + + pm.run_manual2sub(); // Reduce manual 2's complement to subtraction first + pm.run_sub2neg(); + pm.run_negexpand(); + pm.run_negneg(); + pm.run_negmux(); + } + } + + if (run_post) { + did_something = true; + while (did_something) { + did_something = false; + peepopt_pm pm(module); + pm.setup(module->selected_cells()); + + pm.run_negrebuild(); + pm.run_muxneg(); + pm.run_neg2sub(); + } + } + } + } +} NegoptPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/silimate/peepopt_manual2sub.pmg b/passes/silimate/peepopt_manual2sub.pmg new file mode 100644 index 000000000..8b456fa76 --- /dev/null +++ b/passes/silimate/peepopt_manual2sub.pmg @@ -0,0 +1,171 @@ +pattern manual2sub +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Canonicalize manual 2's complement subtraction: +// Case A: (a + ~b) + 1 ===> a - b +// Case B: a + (~b + 1) ===> a - b +// +// Note: Fanout checking includes module connections to avoid breaking +// designs where intermediate results are used by output assignments. +// + +state minuend subtrahend result_sig +state is_signed +state inner_y + +// 1. Match the "root" add (the one that produces the final result) +match root_add + select root_add->type == $add + set result_sig port(root_add, \Y) + set is_signed root_add->getParam(ID::A_SIGNED).as_bool() +endmatch + +// 2. Case A: (a + ~b) + 1 +// Check if root_add has a constant 1 +code root_add inner_y +{ + SigSpec pa = root_add->getPort(ID::A); + SigSpec pb = root_add->getPort(ID::B); + + auto is_one = [](SigSpec s) { + if (!s.is_fully_const()) return false; + Const c = s.as_const(); + for (int i = 0; i < c.size(); i++) { + if (i == 0 && c[i] != State::S1) return false; + if (i > 0 && c[i] != State::S0) return false; + } + return true; + }; + + if (is_one(pa)) { + inner_y = pb; + } else if (is_one(pb)) { + inner_y = pa; + } else { + branch; + reject; + } +} +endcode + +// Find the inner add +match inner_add_A + select inner_add_A->type == $add + index port(inner_add_A, \Y) === inner_y + select nusers(port(inner_add_A, \Y)) == 2 +endmatch + +// Find the NOT gate on one of the ports of inner_add_A +match not_gate_A + select not_gate_A->type == $not + filter not_gate_A->getPort(ID::Y) == inner_add_A->getPort(ID::A) || not_gate_A->getPort(ID::Y) == inner_add_A->getPort(ID::B) + set subtrahend port(not_gate_A, \A) +endmatch + +code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed +{ + + // Require consistent signedness on the root add. + if (root_add->getParam(ID::B_SIGNED).as_bool() != is_signed) + reject; + + if (inner_add_A->getParam(ID::A_SIGNED).as_bool() != is_signed) + reject; + if (inner_add_A->getParam(ID::B_SIGNED).as_bool() != is_signed) + reject; + + if (not_gate_A->getPort(ID::Y) == inner_add_A->getPort(ID::A)) + minuend = inner_add_A->getPort(ID::B); + else + minuend = inner_add_A->getPort(ID::A); + + // Create the subtraction cell + log("manual2sub in %s: Found (a + ~b) + 1 pattern, creating $sub for %s\n", log_id(module), log_signal(result_sig)); + Cell *sub = module->addSub(NEW_ID, minuend, subtrahend, result_sig, is_signed); + + // Let fixup_parameters handle width adjustments + sub->fixup_parameters(); + + // Remove old cells + autoremove(root_add); + autoremove(inner_add_A); + autoremove(not_gate_A); + + did_something = true; + accept; +} +endcode + +// 3. Case B: a + (~b + 1) +code root_add + // Just fall through to the next match +endcode + +// Find the inner add on either port of root_add +match inner_add_B + select inner_add_B->type == $add + filter inner_add_B->getPort(ID::Y) == root_add->getPort(ID::A) || inner_add_B->getPort(ID::Y) == root_add->getPort(ID::B) + select nusers(port(inner_add_B, \Y)) == 2 +endmatch + +// Check if inner_add_B has a constant 1 and a NOT gate +match not_gate_B + select not_gate_B->type == $not + filter not_gate_B->getPort(ID::Y) == inner_add_B->getPort(ID::A) || not_gate_B->getPort(ID::Y) == inner_add_B->getPort(ID::B) +endmatch + +code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed +{ + + // Require consistent signedness on the root add. + if (root_add->getParam(ID::B_SIGNED).as_bool() != is_signed) + reject; + + if (inner_add_B->getParam(ID::A_SIGNED).as_bool() != is_signed) + reject; + if (inner_add_B->getParam(ID::B_SIGNED).as_bool() != is_signed) + reject; + + SigSpec pa = inner_add_B->getPort(ID::A); + SigSpec pb = inner_add_B->getPort(ID::B); + SigSpec not_y = not_gate_B->getPort(ID::Y); + + auto is_one = [](SigSpec s) { + if (!s.is_fully_const()) return false; + Const c = s.as_const(); + for (int i = 0; i < c.size(); i++) { + if (i == 0 && c[i] != State::S1) return false; + if (i > 0 && c[i] != State::S0) return false; + } + return true; + }; + + bool valid = false; + if (is_one(pa) && pb == not_y) valid = true; + if (is_one(pb) && pa == not_y) valid = true; + + if (!valid) reject; + + subtrahend = not_gate_B->getPort(ID::A); + if (inner_add_B->getPort(ID::Y) == root_add->getPort(ID::A)) + minuend = root_add->getPort(ID::B); + else + minuend = root_add->getPort(ID::A); + + // Create the subtraction cell + log("manual2sub in %s: Found a + (~b + 1) pattern, creating $sub for %s\n", log_id(module), log_signal(result_sig)); + Cell *sub = module->addSub(NEW_ID, minuend, subtrahend, result_sig, is_signed); + + // Let fixup_parameters handle width adjustments + sub->fixup_parameters(); + + // Remove old cells + autoremove(root_add); + autoremove(inner_add_B); + autoremove(not_gate_B); + + did_something = true; + accept; +} +endcode diff --git a/passes/silimate/peepopt_muxneg.pmg b/passes/silimate/peepopt_muxneg.pmg new file mode 100644 index 000000000..a45a42489 --- /dev/null +++ b/passes/silimate/peepopt_muxneg.pmg @@ -0,0 +1,59 @@ +pattern muxneg +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Factor negation out of mux +// +// s ? (-a) : (-b) ===> -(s ? a : b) +// + +state mux_a mux_b mux_s mux_y neg_a_in neg_b_in +state neg_a_signed neg_b_signed + +match mux + select mux->type == $mux + set mux_a port(mux, \A) + set mux_b port(mux, \B) + set mux_s port(mux, \S) + set mux_y port(mux, \Y) +endmatch + +match neg_a + select neg_a->type == $neg + select nusers(port(neg_a, \Y)) == 2 + index port(neg_a, \Y) === mux_a + set neg_a_in port(neg_a, \A) + set neg_a_signed neg_a->getParam(\A_SIGNED).as_bool() +endmatch + +match neg_b + select neg_b->type == $neg + select nusers(port(neg_b, \Y)) == 2 + index port(neg_b, \Y) === mux_b + set neg_b_in port(neg_b, \A) + set neg_b_signed neg_b->getParam(\A_SIGNED).as_bool() +endmatch + +code mux_a mux_b mux_s mux_y neg_a_in neg_b_in neg_a_signed neg_b_signed + if (neg_a_signed != neg_b_signed) + reject; + + { + int width = GetSize(mux_y); + + SigSpec mux_out = module->addWire(NEW_ID, width); + Cell *new_mux = module->addMux(NEW_ID, neg_a_in, neg_b_in, mux_s, mux_out); + Cell *new_neg = module->addNeg(NEW_ID, mux_out, mux_y, neg_a_signed); + + log("muxneg pattern in %s: mux=%s, neg_a=%s, neg_b=%s\n", + log_id(module), log_id(mux), log_id(neg_a), log_id(neg_b)); + + new_mux->fixup_parameters(); + new_neg->fixup_parameters(); + autoremove(mux); + autoremove(neg_a); + autoremove(neg_b); + did_something = true; + } + accept; +endcode diff --git a/passes/silimate/peepopt_neg2sub.pmg b/passes/silimate/peepopt_neg2sub.pmg new file mode 100644 index 000000000..c811c6785 --- /dev/null +++ b/passes/silimate/peepopt_neg2sub.pmg @@ -0,0 +1,60 @@ +pattern neg2sub +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Convert addition with negation back to subtraction +// +// a + (-b) ===> a - b +// + +state add_a add_b add_y neg_a +state add_a_signed add_b_signed neg_signed +state neg_on_a + +match add + select add->type == $add + set add_a port(add, \A) + set add_b port(add, \B) + set add_y port(add, \Y) + set add_a_signed add->getParam(\A_SIGNED).as_bool() + set add_b_signed add->getParam(\B_SIGNED).as_bool() +endmatch + +code neg_on_a + neg_on_a = true; + branch; + neg_on_a = false; +endcode + +match neg + select neg->type == $neg + select nusers(port(neg, \Y)) == 2 + index port(neg, \Y) === (neg_on_a ? add_a : add_b) + set neg_a port(neg, \A) + set neg_signed neg->getParam(\A_SIGNED).as_bool() +endmatch + +code add_a add_b add_y neg_a neg_on_a add_a_signed add_b_signed neg_signed + if (add_a_signed != add_b_signed) + reject; + + if (neg_signed != (neg_on_a ? add_a_signed : add_b_signed)) + reject; + + { + Cell *new_sub; + if (neg_on_a) + new_sub = module->addSub(NEW_ID, add_b, neg_a, add_y, add_b_signed); + else + new_sub = module->addSub(NEW_ID, add_a, neg_a, add_y, add_a_signed); + + log("neg2sub pattern in %s: add=%s, neg=%s\n", + log_id(module), log_id(add), log_id(neg)); + + new_sub->fixup_parameters(); + autoremove(add); + autoremove(neg); + did_something = true; + } + accept; +endcode diff --git a/passes/silimate/peepopt_negexpand.pmg b/passes/silimate/peepopt_negexpand.pmg new file mode 100644 index 000000000..3fa3c65dd --- /dev/null +++ b/passes/silimate/peepopt_negexpand.pmg @@ -0,0 +1,56 @@ +pattern negexpand +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Expand negation over addition +// +// -(a + b) ===> (-a) + (-b) +// + +state neg_a neg_y add_a add_b +state a_signed + +match neg + select neg->type == $neg + set neg_a port(neg, \A) + set neg_y port(neg, \Y) + set a_signed neg->getParam(\A_SIGNED).as_bool() +endmatch + +match add + select add->type == $add + index port(add, \Y) === neg_a + select nusers(port(add, \Y)) == 2 + set add_a port(add, \A) + set add_b port(add, \B) +endmatch + +code neg_a neg_y add_a add_b a_signed + if (add->getParam(\A_SIGNED).as_bool() != a_signed) + reject; + if (add->getParam(\B_SIGNED).as_bool() != a_signed) + reject; + + { + // Use output width for negations to handle overflow correctly + int width = GetSize(neg_y); + SigSpec neg_add_a = module->addWire(NEW_ID, width); + Cell *neg_a_cell = module->addNeg(NEW_ID, add_a, neg_add_a, a_signed); + + SigSpec neg_add_b = module->addWire(NEW_ID, width); + Cell *neg_b_cell = module->addNeg(NEW_ID, add_b, neg_add_b, a_signed); + + Cell *new_add = module->addAdd(NEW_ID, neg_add_a, neg_add_b, neg_y, a_signed); + + log("negexpand pattern in %s: neg=%s, add=%s\n", + log_id(module), log_id(neg), log_id(add)); + + neg_a_cell->fixup_parameters(); + neg_b_cell->fixup_parameters(); + new_add->fixup_parameters(); + autoremove(neg); + autoremove(add); + did_something = true; + } + accept; +endcode diff --git a/passes/silimate/peepopt_negmux.pmg b/passes/silimate/peepopt_negmux.pmg new file mode 100644 index 000000000..3b6f5bd55 --- /dev/null +++ b/passes/silimate/peepopt_negmux.pmg @@ -0,0 +1,52 @@ +pattern negmux +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Distribute negation over mux +// +// -(s ? a : b) ===> s ? (-a) : (-b) +// + +state neg_a neg_y mux_a mux_b mux_s +state a_signed + +match neg + select neg->type == $neg + set neg_a port(neg, \A) + set neg_y port(neg, \Y) + set a_signed neg->getParam(\A_SIGNED).as_bool() +endmatch + +match mux + select mux->type == $mux + index port(mux, \Y) === neg_a + select nusers(port(mux, \Y)) == 2 + set mux_a port(mux, \A) + set mux_b port(mux, \B) + set mux_s port(mux, \S) +endmatch + +code neg_a neg_y mux_a mux_b mux_s a_signed + { + int width = GetSize(neg_y); + + SigSpec neg_mux_a = module->addWire(NEW_ID, width); + Cell *neg_a_cell = module->addNeg(NEW_ID, mux_a, neg_mux_a, a_signed); + + SigSpec neg_mux_b = module->addWire(NEW_ID, width); + Cell *neg_b_cell = module->addNeg(NEW_ID, mux_b, neg_mux_b, a_signed); + + Cell *new_mux = module->addMux(NEW_ID, neg_mux_a, neg_mux_b, mux_s, neg_y); + + log("negmux pattern in %s: neg=%s, mux=%s\n", + log_id(module), log_id(neg), log_id(mux)); + + neg_a_cell->fixup_parameters(); + neg_b_cell->fixup_parameters(); + new_mux->fixup_parameters(); + autoremove(neg); + autoremove(mux); + did_something = true; + } + accept; +endcode diff --git a/passes/silimate/peepopt_negneg.pmg b/passes/silimate/peepopt_negneg.pmg new file mode 100644 index 000000000..15f61dd04 --- /dev/null +++ b/passes/silimate/peepopt_negneg.pmg @@ -0,0 +1,35 @@ +pattern negneg +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Simplify double negation +// +// -(-a) ===> a +// + +state neg1_a neg1_y neg2_a + +match neg1 + select neg1->type == $neg + set neg1_a port(neg1, \A) + set neg1_y port(neg1, \Y) +endmatch + +match neg2 + select neg2->type == $neg + index port(neg2, \Y) === neg1_a + select nusers(port(neg2, \Y)) == 2 + set neg2_a port(neg2, \A) +endmatch + +code neg1_a neg1_y neg2_a + module->connect(neg1_y, neg2_a); + + log("negneg pattern in %s: neg1=%s, neg2=%s\n", + log_id(module), log_id(neg1), log_id(neg2)); + + autoremove(neg1); + autoremove(neg2); + did_something = true; + accept; +endcode diff --git a/passes/silimate/peepopt_negrebuild.pmg b/passes/silimate/peepopt_negrebuild.pmg new file mode 100644 index 000000000..462943bfe --- /dev/null +++ b/passes/silimate/peepopt_negrebuild.pmg @@ -0,0 +1,63 @@ +pattern negrebuild +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Rebuild addition of negations into negation of addition +// +// (-a) + (-b) ===> -(a + b) +// + +state add_a add_b add_y neg1_a neg2_a +state add_signed add_b_signed neg1_signed neg2_signed + +match add + select add->type == $add + set add_a port(add, \A) + set add_b port(add, \B) + set add_y port(add, \Y) + set add_signed add->getParam(\A_SIGNED).as_bool() + set add_b_signed add->getParam(\B_SIGNED).as_bool() +endmatch + +match neg1 + select neg1->type == $neg + select nusers(port(neg1, \Y)) == 2 + index port(neg1, \Y) === add_a + set neg1_a port(neg1, \A) + set neg1_signed neg1->getParam(\A_SIGNED).as_bool() +endmatch + +match neg2 + select neg2->type == $neg + select nusers(port(neg2, \Y)) == 2 + index port(neg2, \Y) === add_b + set neg2_a port(neg2, \A) + set neg2_signed neg2->getParam(\A_SIGNED).as_bool() +endmatch + +code add_a add_b add_y neg1_a neg2_a add_signed add_b_signed neg1_signed neg2_signed + if (add_signed != add_b_signed) + reject; + + if (neg1_signed != add_signed || neg2_signed != add_signed) + reject; + + { + int width = GetSize(add_y); + + SigSpec sum = module->addWire(NEW_ID, width); + Cell *new_add = module->addAdd(NEW_ID, neg1_a, neg2_a, sum, add_signed); + Cell *new_neg = module->addNeg(NEW_ID, sum, add_y, add_signed); + + log("negrebuild pattern in %s: add=%s, neg1=%s, neg2=%s\n", + log_id(module), log_id(add), log_id(neg1), log_id(neg2)); + + new_add->fixup_parameters(); + new_neg->fixup_parameters(); + autoremove(add); + autoremove(neg1); + autoremove(neg2); + did_something = true; + } + accept; +endcode diff --git a/passes/silimate/peepopt_sub2neg.pmg b/passes/silimate/peepopt_sub2neg.pmg new file mode 100644 index 000000000..ffb273f65 --- /dev/null +++ b/passes/silimate/peepopt_sub2neg.pmg @@ -0,0 +1,42 @@ +pattern sub2neg +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Convert subtraction to addition with negation +// +// a - b ===> a + (-b) +// + +state sub_a sub_b sub_y +state a_signed b_signed + +match sub + select sub->type == $sub + filter !sub->getPort(\B).is_fully_const() + set sub_a port(sub, \A) + set sub_b port(sub, \B) + set sub_y port(sub, \Y) + set a_signed sub->getParam(\A_SIGNED).as_bool() + set b_signed sub->getParam(\B_SIGNED).as_bool() +endmatch + +code sub_a sub_b sub_y a_signed b_signed + if (a_signed != b_signed) + reject; + + { + int width = GetSize(sub_y); + SigSpec neg_y = module->addWire(NEW_ID, width); + Cell *neg = module->addNeg(NEW_ID, sub_b, neg_y, b_signed); + Cell *add = module->addAdd(NEW_ID, sub_a, neg_y, sub_y, a_signed); + + log("sub2neg pattern in %s: sub=%s -> neg=%s, add=%s\n", + log_id(module), log_id(sub), log_id(neg), log_id(add)); + + neg->fixup_parameters(); + add->fixup_parameters(); + autoremove(sub); + did_something = true; + } + accept; +endcode diff --git a/tests/silimate/manual2sub.ys b/tests/silimate/manual2sub.ys new file mode 100644 index 000000000..a93896500 --- /dev/null +++ b/tests/silimate/manual2sub.ys @@ -0,0 +1,73 @@ +log -header "Positive case: (a + ~b) + 1 => a - b => a + (-b)" +log -push +design -reset +read_verilog < a - b => a + (-b)" +log -push +design -reset +read_verilog < Date: Tue, 3 Feb 2026 22:47:20 +0000 Subject: [PATCH 16/21] Only reuse ABC processes if we're using yosys-abc and it was built with ENABLE_READLINE (cherry picked from commit 5054fd17d7b70f2df97360bb0f0cc1c92a6ffe72) --- passes/techmap/abc.cc | 99 +++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 36 deletions(-) diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index e9d02b85c..f7fa095a0 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -143,6 +143,14 @@ struct AbcConfig bool markgroups = false; pool enabled_gates; bool cmos_cost = false; + + bool is_yosys_abc() const { +#ifdef ABCEXTERNAL + return false; +#else + return exe_file == yosys_abc_executable; +#endif + } }; struct AbcSigVal { @@ -155,7 +163,12 @@ struct AbcSigVal { } }; -#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN) +// REUSE_YOSYS_ABC_PROCESSES only works when ABC is built with ENABLE_READLINE. +#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN) && defined(YOSYS_ENABLE_READLINE) +#define REUSE_YOSYS_ABC_PROCESSES +#endif + +#ifdef REUSE_YOSYS_ABC_PROCESSES struct AbcProcess { pid_t pid; @@ -1063,8 +1076,9 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module abc_script += stringf("; dress \"%s/input.blif\"", run_abc.tempdir_name); abc_script += stringf("; write_blif %s/output.blif", run_abc.tempdir_name); abc_script = add_echos_to_abc_cmd(abc_script); -#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN) - abc_script += "; echo; echo \"YOSYS_ABC_DONE\"\n"; +#if defined(REUSE_YOSYS_ABC_PROCESSES) + if (config.is_yosys_abc()) + abc_script += "; echo; echo \"YOSYS_ABC_DONE\"\n"; #endif for (size_t i = 0; i+1 < abc_script.size(); i++) @@ -1127,6 +1141,7 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module handle_loops(assign_map, module); } +#if defined(REUSE_YOSYS_ABC_PROCESSES) static bool is_abc_prompt(const std::string &line, std::string &rest) { size_t pos = 0; while (true) { @@ -1197,8 +1212,13 @@ bool read_until_abc_done(abc_output_filter &filt, int fd, DeferredLogs &logs) { line.append(start, end - start); } } +#endif +#if defined(REUSE_YOSYS_ABC_PROCESSES) void RunAbcState::run(ConcurrentStack &process_pool) +#else +void RunAbcState::run(ConcurrentStack &) +#endif { std::string buffer = stringf("%s/input.blif", tempdir_name); FILE *f = fopen(buffer.c_str(), "wt"); @@ -1323,9 +1343,13 @@ void RunAbcState::run(ConcurrentStack &process_pool) logs.log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n", count_gates, GetSize(signal_list), count_input, count_output); - if (count_output > 0) - { - std::string tmp_script_name = stringf("%s/abc.script", tempdir_name); + if (count_output == 0) { + log("Don't call ABC as there is nothing to map.\n"); + return; + } + int ret; + std::string tmp_script_name = stringf("%s/abc.script", tempdir_name); + do { logs.log("Running ABC script: %s\n", replace_tempdir(tmp_script_name, tempdir_name, config.show_tempdir)); errno = 0; @@ -1356,7 +1380,7 @@ void RunAbcState::run(ConcurrentStack &process_pool) abc_argv[2] = strdup("-f"); abc_argv[3] = strdup(tmp_script_name.c_str()); abc_argv[4] = 0; - int ret = abc::Abc_RealMain(4, abc_argv); + ret = abc::Abc_RealMain(4, abc_argv); free(abc_argv[0]); free(abc_argv[1]); free(abc_argv[2]); @@ -1371,39 +1395,42 @@ void RunAbcState::run(ConcurrentStack &process_pool) for (std::string line; std::getline(temp_stdouterr_r, line); ) filt.next_line(line + "\n"); temp_stdouterr_r.close(); -#elif defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN) - AbcProcess process; - if (std::optional process_opt = process_pool.try_pop_back()) { - process = std::move(process_opt.value()); - } else if (std::optional process_opt = spawn_abc(config.exe_file.c_str(), logs)) { - process = std::move(process_opt.value()); - } else { - return; - } - std::string cmd = stringf( - "empty\n" - "source %s\n", tmp_script_name); - int ret = write(process.to_child_pipe, cmd.c_str(), cmd.size()); - if (ret != static_cast(cmd.size())) { - logs.log_error("write failed"); - return; - } - ret = read_until_abc_done(filt, process.from_child_pipe, logs) ? 0 : 1; - if (ret == 0) { - process_pool.push_back(std::move(process)); - } + break; #else - std::string cmd = stringf("\"%s\" -s -f %s/abc.script 2>&1", config.exe_file.c_str(), tempdir_name.c_str()); - int ret = run_command(cmd, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1)); -#endif - if (ret != 0) { - logs.log_error("ABC: execution of script \"%s\" failed: return code %d (errno=%d).\n", tmp_script_name, ret, errno); - return; +#if defined(REUSE_YOSYS_ABC_PROCESSES) + if (config.is_yosys_abc()) { + AbcProcess process; + if (std::optional process_opt = process_pool.try_pop_back()) { + process = std::move(process_opt.value()); + } else if (std::optional process_opt = spawn_abc(config.exe_file.c_str(), logs)) { + process = std::move(process_opt.value()); + } else { + return; + } + std::string cmd = stringf( + "empty\n" + "source %s\n", tmp_script_name); + ret = write(process.to_child_pipe, cmd.c_str(), cmd.size()); + if (ret != static_cast(cmd.size())) { + logs.log_error("write failed"); + return; + } + ret = read_until_abc_done(filt, process.from_child_pipe, logs) ? 0 : 1; + if (ret == 0) { + process_pool.push_back(std::move(process)); + } + break; } - did_run = true; +#endif + std::string cmd = stringf("\"%s\" -s -f %s 2>&1", config.exe_file, tmp_script_name); + ret = run_command(cmd, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1)); +#endif + } while (false); + if (ret != 0) { + logs.log_error("ABC: execution of script \"%s\" failed: return code %d (errno=%d).\n", tmp_script_name, ret, errno); return; } - log("Don't call ABC as there is nothing to map.\n"); + did_run = true; } void emit_global_input_files(const AbcConfig &config) From 807df40422e0d2ec072cb3b4c36ddf2a1fd16dd9 Mon Sep 17 00:00:00 2001 From: Akash Levy Date: Tue, 3 Feb 2026 23:21:48 -0800 Subject: [PATCH 17/21] Undo the weird abc changes --- frontends/verific/verific.cc | 4 -- passes/techmap/abc.cc | 88 ++++++++++++------------------------ 2 files changed, 28 insertions(+), 64 deletions(-) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 3b17512b1..9a5ffc767 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -3143,9 +3143,6 @@ std::string verific_import(Design *design, const std::mapfirst.c_str()); - // log(" Inferring clock enable muxes for %s.\n", it->first.c_str()); - // nl->InferClockEnableMux(); - log(" Running post-elaboration for %s.\n", it->first.c_str()); nl->PostElaborationProcess(); @@ -3866,7 +3863,6 @@ struct VerificPass : public Pass { unsigned verilog_mode = veri_file::UNDEFINED; #ifdef VERIFIC_VHDL_SUPPORT unsigned vhdl_mode = vhdl_file::UNDEFINED; - bool is_formal = false; #endif bool is_formal = false; #else diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 52576a8ee..9b22c38f0 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -191,10 +191,10 @@ struct AbcProcess int status; int ret = waitpid(pid, &status, 0); if (ret != pid) { - log_error("waitpid(%d) failed\n", pid); + log_error("waitpid(%d) failed", pid); } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - log_error("ABC failed with status %X\n", status); + log_error("ABC failed with status %X", status); } if (from_child_pipe >= 0) close(from_child_pipe); @@ -206,12 +206,12 @@ std::optional spawn_abc(const char* abc_exe, DeferredLogs &logs) { // fork()s. int to_child_pipe[2]; if (pipe2(to_child_pipe, O_CLOEXEC) != 0) { - logs.log_error("pipe failed\n"); + logs.log_error("pipe failed"); return std::nullopt; } int from_child_pipe[2]; if (pipe2(from_child_pipe, O_CLOEXEC) != 0) { - logs.log_error("pipe failed\n"); + logs.log_error("pipe failed"); return std::nullopt; } @@ -224,39 +224,39 @@ std::optional spawn_abc(const char* abc_exe, DeferredLogs &logs) { posix_spawn_file_actions_t file_actions; if (posix_spawn_file_actions_init(&file_actions) != 0) { - logs.log_error("posix_spawn_file_actions_init failed\n"); + logs.log_error("posix_spawn_file_actions_init failed"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, to_child_pipe[1]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed\n"); + logs.log_error("posix_spawn_file_actions_addclose failed"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, from_child_pipe[0]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed\n"); + logs.log_error("posix_spawn_file_actions_addclose failed"); return std::nullopt; } if (posix_spawn_file_actions_adddup2(&file_actions, to_child_pipe[0], STDIN_FILENO) != 0) { - logs.log_error("posix_spawn_file_actions_adddup2 failed\n"); + logs.log_error("posix_spawn_file_actions_adddup2 failed"); return std::nullopt; } if (posix_spawn_file_actions_adddup2(&file_actions, from_child_pipe[1], STDOUT_FILENO) != 0) { - logs.log_error("posix_spawn_file_actions_adddup2 failed\n"); + logs.log_error("posix_spawn_file_actions_adddup2 failed"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, to_child_pipe[0]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed\n"); + logs.log_error("posix_spawn_file_actions_addclose failed"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, from_child_pipe[1]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed\n"); + logs.log_error("posix_spawn_file_actions_addclose failed"); return std::nullopt; } char arg1[] = "-s"; char* argv[] = { strdup(abc_exe), arg1, nullptr }; if (0 != posix_spawnp(&result.pid, abc_exe, &file_actions, nullptr, argv, environ)) { - logs.log_error("posix_spawnp %s failed (errno=%s)\n", abc_exe, strerror(errno)); + logs.log_error("posix_spawnp %s failed (errno=%s)", abc_exe, strerror(errno)); return std::nullopt; } free(argv[0]); @@ -1124,72 +1124,40 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module handle_loops(assign_map, module); } -static bool is_abc_prompt(const std::string &line, std::string &rest) { - size_t pos = 0; - while (true) { - // The prompt may not start at the start of the line, because - // ABC can output progress and maybe other data that isn't - // newline-terminated. - size_t start = line.find("abc ", pos); - if (start == std::string::npos) - return false; - pos = start + 4; - - size_t digits = 0; - while (pos + digits < line.size() && line[pos + digits] >= '0' && line[pos + digits] <= '9') - ++digits; - if (digits < 2) - return false; - if (line.substr(pos + digits, 2) == "> ") { - rest = line.substr(pos + digits + 2); - return true; - } - } -} - bool read_until_abc_done(abc_output_filter &filt, int fd, DeferredLogs &logs) { std::string line; char buf[1024]; - bool seen_source_cmd = false; - bool seen_yosys_abc_done = false; while (true) { int ret = read(fd, buf, sizeof(buf) - 1); if (ret < 0) { - logs.log_error("Failed to read from ABC, errno=%d\n", errno); + logs.log_error("Failed to read from ABC, errno=%d", errno); return false; } if (ret == 0) { - logs.log_error("ABC exited prematurely\n"); + logs.log_error("ABC exited prematurely"); return false; } char *start = buf; char *end = buf + ret; while (start < end) { char *p = static_cast(memchr(start, '\n', end - start)); - char *upto = p == nullptr ? end : p + 1; - line.append(start, upto - start); - start = upto; - - std::string rest; - bool is_prompt = is_abc_prompt(line, rest); - if (is_prompt && seen_source_cmd) { - // This is the first prompt after we sourced the script. - // We are done here. - // We won't have seen a newline yet since ABC is waiting at the prompt. - if (!seen_yosys_abc_done) - logs.log_error("ABC script did not complete successfully\n"); - return seen_yosys_abc_done; + if (p == nullptr) { + break; } - if (line.empty() || line[line.size() - 1] != '\n') { - // No newline yet, wait for more text - continue; + line.append(start, p + 1 - start); + if (line.substr(0, 14) == "YOSYS_ABC_DONE") { + // Ignore any leftover output, there should only be a prompt perhaps + return true; + } + // If ABC aborted the sourced script, it returns to the prompt and will + // never print YOSYS_ABC_DONE. Treat this as a failed run, not a hang. + if (line.substr(0, 7) == "Error: ") { + logs.log_error("ABC: %s", line.c_str()); + return false; } filt.next_line(line); - if (is_prompt && rest.substr(0, 7) == "source ") - seen_source_cmd = true; - if (line.substr(0, 14) == "YOSYS_ABC_DONE") - seen_yosys_abc_done = true; line.clear(); + start = p + 1; } line.append(start, end - start); } @@ -2876,4 +2844,4 @@ struct AbcPass : public Pass { } } AbcPass; -PRIVATE_NAMESPACE_END +PRIVATE_NAMESPACE_END \ No newline at end of file From ddfa34d7439381a73203702f76c71fbe199c8cd1 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 4 Feb 2026 08:54:38 +0100 Subject: [PATCH 18/21] Next dev cycle --- CHANGELOG | 3 +++ Makefile | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6e2bca32c..e345a8514 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,9 @@ List of major changes and improvements between releases ======================================================= +Yosys 0.62 .. Yosys 0.63-dev +-------------------------- + Yosys 0.61 .. Yosys 0.62 -------------------------- * Various diff --git a/Makefile b/Makefile index 28608f573..364e1ce8d 100644 --- a/Makefile +++ b/Makefile @@ -161,7 +161,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.62 +YOSYS_VER := 0.62+0 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) @@ -186,7 +186,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: -# sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 5ae48ee.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 7326bb7.. | wc -l`/;" Makefile ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) From 23ed2ef5239c339880af6ff85a50abb7cd9c9ed1 Mon Sep 17 00:00:00 2001 From: Akash Levy Date: Tue, 3 Feb 2026 23:55:25 -0800 Subject: [PATCH 19/21] Small abc update to see what happens --- passes/techmap/abc.cc | 55 +++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 9b22c38f0..13f1d0181 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -191,10 +191,10 @@ struct AbcProcess int status; int ret = waitpid(pid, &status, 0); if (ret != pid) { - log_error("waitpid(%d) failed", pid); + log_error("waitpid(%d) failed\n", pid); } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - log_error("ABC failed with status %X", status); + log_error("ABC failed with status %X\n", status); } if (from_child_pipe >= 0) close(from_child_pipe); @@ -206,12 +206,12 @@ std::optional spawn_abc(const char* abc_exe, DeferredLogs &logs) { // fork()s. int to_child_pipe[2]; if (pipe2(to_child_pipe, O_CLOEXEC) != 0) { - logs.log_error("pipe failed"); + logs.log_error("pipe failed\n"); return std::nullopt; } int from_child_pipe[2]; if (pipe2(from_child_pipe, O_CLOEXEC) != 0) { - logs.log_error("pipe failed"); + logs.log_error("pipe failed\n"); return std::nullopt; } @@ -224,39 +224,39 @@ std::optional spawn_abc(const char* abc_exe, DeferredLogs &logs) { posix_spawn_file_actions_t file_actions; if (posix_spawn_file_actions_init(&file_actions) != 0) { - logs.log_error("posix_spawn_file_actions_init failed"); + logs.log_error("posix_spawn_file_actions_init failed\n"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, to_child_pipe[1]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed"); + logs.log_error("posix_spawn_file_actions_addclose failed\n"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, from_child_pipe[0]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed"); + logs.log_error("posix_spawn_file_actions_addclose failed\n"); return std::nullopt; } if (posix_spawn_file_actions_adddup2(&file_actions, to_child_pipe[0], STDIN_FILENO) != 0) { - logs.log_error("posix_spawn_file_actions_adddup2 failed"); + logs.log_error("posix_spawn_file_actions_adddup2 failed\n"); return std::nullopt; } if (posix_spawn_file_actions_adddup2(&file_actions, from_child_pipe[1], STDOUT_FILENO) != 0) { - logs.log_error("posix_spawn_file_actions_adddup2 failed"); + logs.log_error("posix_spawn_file_actions_adddup2 failed\n"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, to_child_pipe[0]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed"); + logs.log_error("posix_spawn_file_actions_addclose failed\n"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, from_child_pipe[1]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed"); + logs.log_error("posix_spawn_file_actions_addclose failed\n"); return std::nullopt; } char arg1[] = "-s"; char* argv[] = { strdup(abc_exe), arg1, nullptr }; if (0 != posix_spawnp(&result.pid, abc_exe, &file_actions, nullptr, argv, environ)) { - logs.log_error("posix_spawnp %s failed (errno=%s)", abc_exe, strerror(errno)); + logs.log_error("posix_spawnp %s failed (errno=%s)\n", abc_exe, strerror(errno)); return std::nullopt; } free(argv[0]); @@ -1124,17 +1124,42 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module handle_loops(assign_map, module); } +static bool is_abc_prompt(const std::string &line, std::string &rest) { + size_t pos = 0; + while (true) { + // The prompt may not start at the start of the line, because + // ABC can output progress and maybe other data that isn't + // newline-terminated. + size_t start = line.find("abc ", pos); + if (start == std::string::npos) + return false; + pos = start + 4; + + size_t digits = 0; + while (pos + digits < line.size() && line[pos + digits] >= '0' && line[pos + digits] <= '9') + ++digits; + if (digits < 2) + return false; + if (line.substr(pos + digits, 2) == "> ") { + rest = line.substr(pos + digits + 2); + return true; + } + } +} + bool read_until_abc_done(abc_output_filter &filt, int fd, DeferredLogs &logs) { std::string line; char buf[1024]; + bool seen_source_cmd = false; + bool seen_yosys_abc_done = false; while (true) { int ret = read(fd, buf, sizeof(buf) - 1); if (ret < 0) { - logs.log_error("Failed to read from ABC, errno=%d", errno); + logs.log_error("Failed to read from ABC, errno=%d\n", errno); return false; } if (ret == 0) { - logs.log_error("ABC exited prematurely"); + logs.log_error("ABC exited prematurely\n"); return false; } char *start = buf; @@ -2844,4 +2869,4 @@ struct AbcPass : public Pass { } } AbcPass; -PRIVATE_NAMESPACE_END \ No newline at end of file +PRIVATE_NAMESPACE_END From c57c49873e21c25d10d19167981995f29e73acd9 Mon Sep 17 00:00:00 2001 From: Akash Levy Date: Wed, 4 Feb 2026 03:48:58 -0800 Subject: [PATCH 20/21] Please just stop modifying yosys... --- passes/techmap/abc.cc | 431 ++++++++---------------------------------- 1 file changed, 79 insertions(+), 352 deletions(-) diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index a91a17dc1..915899b7d 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -136,7 +136,6 @@ struct AbcConfig bool fast_mode = false; bool show_tempdir = false; bool sop_mode = false; - bool word_mode = false; bool abc_dress = false; bool map_mux4 = false; bool map_mux8 = false; @@ -144,16 +143,6 @@ struct AbcConfig bool markgroups = false; pool enabled_gates; bool cmos_cost = false; - int max_threads = -1; // -1 means auto (use number of modules) - int reserved_cores = 4; // cores reserved for main thread and other work - - bool is_yosys_abc() const { -#ifdef ABCEXTERNAL - return false; -#else - return exe_file == yosys_abc_executable; -#endif - } }; struct AbcSigVal { @@ -166,12 +155,7 @@ struct AbcSigVal { } }; -// REUSE_YOSYS_ABC_PROCESSES only works when ABC is built with ENABLE_READLINE. -#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN) && defined(YOSYS_ENABLE_READLINE) -#define REUSE_YOSYS_ABC_PROCESSES -#endif - -#ifdef REUSE_YOSYS_ABC_PROCESSES +#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN) struct AbcProcess { pid_t pid; @@ -204,10 +188,10 @@ struct AbcProcess int status; int ret = waitpid(pid, &status, 0); if (ret != pid) { - log_error("waitpid(%d) failed\n", pid); + log_error("waitpid(%d) failed", pid); } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - log_error("ABC failed with status %X\n", status); + log_error("ABC failed with status %X", status); } if (from_child_pipe >= 0) close(from_child_pipe); @@ -219,12 +203,12 @@ std::optional spawn_abc(const char* abc_exe, DeferredLogs &logs) { // fork()s. int to_child_pipe[2]; if (pipe2(to_child_pipe, O_CLOEXEC) != 0) { - logs.log_error("pipe failed\n"); + logs.log_error("pipe failed"); return std::nullopt; } int from_child_pipe[2]; if (pipe2(from_child_pipe, O_CLOEXEC) != 0) { - logs.log_error("pipe failed\n"); + logs.log_error("pipe failed"); return std::nullopt; } @@ -237,39 +221,39 @@ std::optional spawn_abc(const char* abc_exe, DeferredLogs &logs) { posix_spawn_file_actions_t file_actions; if (posix_spawn_file_actions_init(&file_actions) != 0) { - logs.log_error("posix_spawn_file_actions_init failed\n"); + logs.log_error("posix_spawn_file_actions_init failed"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, to_child_pipe[1]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed\n"); + logs.log_error("posix_spawn_file_actions_addclose failed"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, from_child_pipe[0]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed\n"); + logs.log_error("posix_spawn_file_actions_addclose failed"); return std::nullopt; } if (posix_spawn_file_actions_adddup2(&file_actions, to_child_pipe[0], STDIN_FILENO) != 0) { - logs.log_error("posix_spawn_file_actions_adddup2 failed\n"); + logs.log_error("posix_spawn_file_actions_adddup2 failed"); return std::nullopt; } if (posix_spawn_file_actions_adddup2(&file_actions, from_child_pipe[1], STDOUT_FILENO) != 0) { - logs.log_error("posix_spawn_file_actions_adddup2 failed\n"); + logs.log_error("posix_spawn_file_actions_adddup2 failed"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, to_child_pipe[0]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed\n"); + logs.log_error("posix_spawn_file_actions_addclose failed"); return std::nullopt; } if (posix_spawn_file_actions_addclose(&file_actions, from_child_pipe[1]) != 0) { - logs.log_error("posix_spawn_file_actions_addclose failed\n"); + logs.log_error("posix_spawn_file_actions_addclose failed"); return std::nullopt; } char arg1[] = "-s"; char* argv[] = { strdup(abc_exe), arg1, nullptr }; if (0 != posix_spawnp(&result.pid, abc_exe, &file_actions, nullptr, argv, environ)) { - logs.log_error("posix_spawnp %s failed (errno=%s)\n", abc_exe, strerror(errno)); + logs.log_error("posix_spawnp %s failed (errno=%s)", abc_exe, strerror(errno)); return std::nullopt; } free(argv[0]); @@ -329,7 +313,7 @@ struct AbcModuleState { void handle_loops(AbcSigMap &assign_map, RTLIL::Module *module); void prepare_module(RTLIL::Design *design, RTLIL::Module *module, AbcSigMap &assign_map, const std::vector &cells, bool dff_mode, std::string clk_str); - void extract(AbcSigMap &assign_map, dict &sig2src, SigMap &orig_sigmap, RTLIL::Design *design, RTLIL::Module *module); + void extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL::Module *module); void finish(); }; @@ -594,8 +578,12 @@ bool AbcModuleState::extract_cell(const AbcSigMap &assign_map, RTLIL::Module *mo std::string AbcModuleState::remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire) { std::string abc_sname = abc_name.substr(1); - if (abc_sname.compare(0, 9, "new_ys__n") == 0) + bool isnew = false; + if (abc_sname.compare(0, 4, "new_") == 0) + { abc_sname.erase(0, 4); + isnew = true; + } if (abc_sname.compare(0, 5, "ys__n") == 0) { abc_sname.erase(0, 5); @@ -610,9 +598,11 @@ std::string AbcModuleState::remap_name(RTLIL::IdString abc_name, RTLIL::Wire **o const auto &bit = signal_bits.at(sid); if (bit.wire != nullptr) { - std::string s = stringf("\\%s_ix%d", bit.wire->name.c_str()+1, map_autoidx); // SILIMATE: Improve the naming + std::string s = stringf("$abc$%d$%s", map_autoidx, bit.wire->name.c_str()+1); if (bit.wire->width != 1) s += stringf("[%d]", bit.offset); + if (isnew) + s += "_new"; s += postfix; if (orig_wire != nullptr) *orig_wire = bit.wire; @@ -621,7 +611,7 @@ std::string AbcModuleState::remap_name(RTLIL::IdString abc_name, RTLIL::Wire **o } } } - return stringf("\\%s_ix%d", abc_sname.c_str(), map_autoidx); // SILIMATE: Improve the naming + return stringf("$abc$%d$%s", map_autoidx, abc_name.substr(1)); } void AbcModuleState::dump_loop_graph(FILE *f, int &nr, dict> &edges, pool &workpool, std::vector &in_counts) @@ -1073,9 +1063,8 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module abc_script += stringf("; dress \"%s/input.blif\"", run_abc.tempdir_name); abc_script += stringf("; write_blif %s/output.blif", run_abc.tempdir_name); abc_script = add_echos_to_abc_cmd(abc_script); -#if defined(REUSE_YOSYS_ABC_PROCESSES) - if (config.is_yosys_abc()) - abc_script += "; echo; echo \"YOSYS_ABC_DONE\"\n"; +#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN) + abc_script += "; echo; echo \"YOSYS_ABC_DONE\"\n"; #endif for (size_t i = 0; i+1 < abc_script.size(); i++) @@ -1138,43 +1127,17 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module handle_loops(assign_map, module); } -#if defined(REUSE_YOSYS_ABC_PROCESSES) -static bool is_abc_prompt(const std::string &line, std::string &rest) { - size_t pos = 0; - while (true) { - // The prompt may not start at the start of the line, because - // ABC can output progress and maybe other data that isn't - // newline-terminated. - size_t start = line.find("abc ", pos); - if (start == std::string::npos) - return false; - pos = start + 4; - - size_t digits = 0; - while (pos + digits < line.size() && line[pos + digits] >= '0' && line[pos + digits] <= '9') - ++digits; - if (digits < 2) - return false; - if (line.substr(pos + digits, 2) == "> ") { - rest = line.substr(pos + digits + 2); - return true; - } - } -} - bool read_until_abc_done(abc_output_filter &filt, int fd, DeferredLogs &logs) { std::string line; char buf[1024]; - bool seen_source_cmd = false; - bool seen_yosys_abc_done = false; while (true) { int ret = read(fd, buf, sizeof(buf) - 1); if (ret < 0) { - logs.log_error("Failed to read from ABC, errno=%d\n", errno); + logs.log_error("Failed to read from ABC, errno=%d", errno); return false; } if (ret == 0) { - logs.log_error("ABC exited prematurely\n"); + logs.log_error("ABC exited prematurely"); return false; } char *start = buf; @@ -1202,13 +1165,8 @@ bool read_until_abc_done(abc_output_filter &filt, int fd, DeferredLogs &logs) { line.append(start, end - start); } } -#endif -#if defined(REUSE_YOSYS_ABC_PROCESSES) void RunAbcState::run(ConcurrentStack &process_pool) -#else -void RunAbcState::run(ConcurrentStack &) -#endif { std::string buffer = stringf("%s/input.blif", tempdir_name); FILE *f = fopen(buffer.c_str(), "wt"); @@ -1333,13 +1291,9 @@ void RunAbcState::run(ConcurrentStack &) logs.log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n", count_gates, GetSize(signal_list), count_input, count_output); - if (count_output == 0) { - log("Don't call ABC as there is nothing to map.\n"); - return; - } - int ret; - std::string tmp_script_name = stringf("%s/abc.script", tempdir_name); - do { + if (count_output > 0) + { + std::string tmp_script_name = stringf("%s/abc.script", tempdir_name); logs.log("Running ABC script: %s\n", replace_tempdir(tmp_script_name, tempdir_name, config.show_tempdir)); errno = 0; @@ -1370,7 +1324,7 @@ void RunAbcState::run(ConcurrentStack &) abc_argv[2] = strdup("-f"); abc_argv[3] = strdup(tmp_script_name.c_str()); abc_argv[4] = 0; - ret = abc::Abc_RealMain(4, abc_argv); + int ret = abc::Abc_RealMain(4, abc_argv); free(abc_argv[0]); free(abc_argv[1]); free(abc_argv[2]); @@ -1385,43 +1339,39 @@ void RunAbcState::run(ConcurrentStack &) for (std::string line; std::getline(temp_stdouterr_r, line); ) filt.next_line(line + "\n"); temp_stdouterr_r.close(); - break; -#else -#if defined(REUSE_YOSYS_ABC_PROCESSES) - if (config.is_yosys_abc()) { - AbcProcess process; - if (std::optional process_opt = process_pool.try_pop_back()) { - process = std::move(process_opt.value()); - } else if (std::optional process_opt = spawn_abc(config.exe_file.c_str(), logs)) { - process = std::move(process_opt.value()); - } else { - return; - } - std::string cmd = stringf( - "empty\n" - "source %s\n", tmp_script_name); - ret = write(process.to_child_pipe, cmd.c_str(), cmd.size()); - if (ret != static_cast(cmd.size())) { - logs.log_error("write failed"); - return; - } - ret = read_until_abc_done(filt, process.from_child_pipe, logs) ? 0 : 1; - if (ret == 0) { - process_pool.push_back(std::move(process)); - } - break; +#elif defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN) + AbcProcess process; + if (std::optional process_opt = process_pool.try_pop_back()) { + process = std::move(process_opt.value()); + } else if (std::optional process_opt = spawn_abc(config.exe_file.c_str(), logs)) { + process = std::move(process_opt.value()); + } else { + return; } + std::string cmd = stringf( + "empty\n" + "source %s\n", tmp_script_name); + int ret = write(process.to_child_pipe, cmd.c_str(), cmd.size()); + if (ret != static_cast(cmd.size())) { + logs.log_error("write failed"); + return; + } + ret = read_until_abc_done(filt, process.from_child_pipe, logs) ? 0 : 1; + if (ret == 0) { + process_pool.push_back(std::move(process)); + } +#else + std::string cmd = stringf("\"%s\" -s -f %s/abc.script 2>&1", config.exe_file.c_str(), tempdir_name.c_str()); + int ret = run_command(cmd, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1)); #endif - std::string cmd = stringf("\"%s\" -s -f %s 2>&1", config.exe_file, tmp_script_name); - ret = run_command(cmd, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1)); -#endif - } while (false); - if (ret != 0) { - logs.log_error("ABC: execution of script \"%s\" failed: return code %d (errno=%d).\n", tmp_script_name, ret, errno); + if (ret != 0) { + logs.log_error("ABC: execution of script \"%s\" failed: return code %d (errno=%d).\n", tmp_script_name, ret, errno); + return; + } + did_run = true; return; } - logs.log("Don't call ABC as there is nothing to map.\n"); - did_run = true; + log("Don't call ABC as there is nothing to map.\n"); } void emit_global_input_files(const AbcConfig &config) @@ -1483,7 +1433,7 @@ void emit_global_input_files(const AbcConfig &config) } } -void AbcModuleState::extract(AbcSigMap &assign_map, dict &sig2src, SigMap &orig_sigmap, RTLIL::Design *design, RTLIL::Module *module) +void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL::Module *module) { log_push(); log_header(design, "Executed ABC.\n"); @@ -1509,39 +1459,22 @@ void AbcModuleState::extract(AbcSigMap &assign_map, dict & RTLIL::Module *mapped_mod = mapped_design->module(ID(netlist)); if (mapped_mod == nullptr) log_error("ABC output file does not contain a module `netlist'.\n"); - SigMap mapped_sigmap(mapped_mod); // SILIMATE: Move mapped sigmap definition here bool markgroups = run_abc.config.markgroups; for (auto w : mapped_mod->wires()) { RTLIL::Wire *orig_wire = nullptr; RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire)); if (orig_wire != nullptr && orig_wire->attributes.count(ID::src)) wire->attributes[ID::src] = orig_wire->attributes[ID::src]; - - // SILIMATE: Apply src attribute to the wire from the original wire - if (orig_wire != nullptr) { - if (sig2src.count(orig_sigmap(orig_wire))) { - wire->set_src_attribute(sig2src[orig_sigmap(orig_wire)]); - sig2src[mapped_sigmap(wire)] = wire->get_src_attribute(); - log_debug("Matched wire %s to driver attributes:\n", orig_wire->name.c_str()); - } else { - log_debug("No driver attributes found for wire %s\n", orig_wire->name.c_str()); - } - } - if (markgroups) wire->attributes[ID::abcgroup] = map_autoidx; design->select(module, wire); } + SigMap mapped_sigmap(mapped_mod); FfInitVals mapped_initvals(&mapped_sigmap, mapped_mod); dict cell_stats; for (auto c : mapped_mod->cells()) { - // SILIMATE: set output port to either Y or Q depending on the cell's ports and apply src attribute to the driver cell - Wire *out_wire = c->getPort((c->hasPort(ID::Y)) ? ID::Y : ID::Q).as_wire(); - Wire *remapped_out_wire = module->wire(remap_name(out_wire->name)); - std::string src_attribute = sig2src[remapped_out_wire]; - if (builtin_lib) { cell_stats[RTLIL::unescape_id(c->type)]++; @@ -1563,57 +1496,32 @@ void AbcModuleState::extract(AbcSigMap &assign_map, dict & continue; } if (c->type == ID(NOT)) { - // SILIMATE: use word-level primitives - RTLIL::Cell *cell = module->addCell(remap_name(c->name), run_abc.config.word_mode ? ID($not) : ID($_NOT_)); + RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_NOT_)); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; for (auto name : {ID::A, ID::Y}) { RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); cell->setPort(name, module->wire(remapped_name)); } - cell->set_src_attribute(src_attribute); // SILIMATE: set src attribute from wire - cell->fixup_parameters(); // SILIMATE: fix up parameters design->select(module, cell); continue; } if (c->type.in(ID(AND), ID(OR), ID(XOR), ID(NAND), ID(NOR), ID(XNOR), ID(ANDNOT), ID(ORNOT))) { - // SILIMATE: use word-level primitives - std::string cell_type; - if (c->type == ID(AND) && run_abc.config.word_mode) - cell_type = "$and"; - else if (c->type == ID(OR) && run_abc.config.word_mode) - cell_type = "$or"; - else if (c->type == ID(XOR) && run_abc.config.word_mode) - cell_type = "$xor"; - else - cell_type = stringf("$_%s_", c->type.c_str()+1); - - RTLIL::Cell *cell = module->addCell(remap_name(c->name), cell_type); + RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; for (auto name : {ID::A, ID::B, ID::Y}) { RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); cell->setPort(name, module->wire(remapped_name)); } - cell->set_src_attribute(src_attribute); // SILIMATE: set src attribute from wire - cell->fixup_parameters(); // SILIMATE: fix up parameters design->select(module, cell); continue; } if (c->type.in(ID(MUX), ID(NMUX))) { - // SILIMATE: use word-level primitives - std::string cell_type; - if (c->type == ID(MUX) && run_abc.config.word_mode) - cell_type = "$mux"; - else - cell_type = stringf("$_%s_", c->type.c_str()+1); - - RTLIL::Cell *cell = module->addCell(remap_name(c->name), cell_type); + RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; for (auto name : {ID::A, ID::B, ID::S, ID::Y}) { RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); cell->setPort(name, module->wire(remapped_name)); } - cell->set_src_attribute(src_attribute); // SILIMATE: set src attribute from wire - cell->fixup_parameters(); // SILIMATE: fix up parameters design->select(module, cell); continue; } @@ -2051,16 +1959,6 @@ struct AbcPass : public Pass { log(" preserve naming by an equivalence check between the original and\n"); log(" post-ABC netlists (experimental).\n"); log("\n"); - log(" -max_threads \n"); - log(" maximum number of worker threads for parallel ABC runs. Default is -1,\n"); - log(" which means auto (use number of modules). Set to 0 to disable parallel\n"); - log(" execution and run everything on the main thread.\n"); - log("\n"); - log(" -reserved_cores \n"); - log(" number of CPU cores to reserve for the main thread and other work.\n"); - log(" Default is 4. The actual number of worker threads used is:\n"); - log(" min(hardware_threads - reserved_cores, max_threads)\n"); - log("\n"); log("When no target cell library is specified the Yosys standard cell library is\n"); log("loaded into ABC before the ABC script is executed.\n"); log("\n"); @@ -2104,7 +2002,6 @@ struct AbcPass : public Pass { lut_arg = design->scratchpad_get_string("abc.lut", lut_arg); luts_arg = design->scratchpad_get_string("abc.luts", luts_arg); config.sop_mode = design->scratchpad_get_bool("abc.sop", false); - config.word_mode = design->scratchpad_get_bool("abc.word", false); config.map_mux4 = design->scratchpad_get_bool("abc.mux4", false); config.map_mux8 = design->scratchpad_get_bool("abc.mux8", false); config.map_mux16 = design->scratchpad_get_bool("abc.mux16", false); @@ -2122,8 +2019,6 @@ struct AbcPass : public Pass { config.cleanup = !design->scratchpad_get_bool("abc.nocleanup", false); config.show_tempdir = design->scratchpad_get_bool("abc.showtmp", false); config.markgroups = design->scratchpad_get_bool("abc.markgroups", false); - config.max_threads = design->scratchpad_get_int("abc.max_threads", -1); - config.reserved_cores = design->scratchpad_get_int("abc.reserved_cores", 4); if (config.cleanup) config.global_tempdir_name = get_base_tmpdir() + "/"; @@ -2202,10 +2097,6 @@ struct AbcPass : public Pass { config.sop_mode = true; continue; } - if (arg == "-word") { - config.word_mode = true; - continue; - } if (arg == "-mux4") { config.map_mux4 = true; continue; @@ -2259,14 +2150,6 @@ struct AbcPass : public Pass { config.markgroups = true; continue; } - if (arg == "-max_threads" && argidx+1 < args.size()) { - config.max_threads = atoi(args[++argidx].c_str()); - continue; - } - if (arg == "-reserved_cores" && argidx+1 < args.size()) { - config.reserved_cores = atoi(args[++argidx].c_str()); - continue; - } break; } extra_args(args, argidx, design); @@ -2474,156 +2357,6 @@ struct AbcPass : public Pass { emit_global_input_files(config); - // Process non-DFF/non-clock-domain mode in stages - if (!dff_mode || !clk_str.empty()) { - // Maps for collateral storage across stages - dict module_assign_maps; - dict module_sigmaps; - dict> module_sig2srcs; - dict module_initvals; - dict module_states; - - // STAGE 1: Compute assign_maps, sig2src maps, and initvals for all modules - // Then prepare for ABC runs (sequential) - for (auto mod : design->selected_modules()) - { - // Do not allow modules with processes - if (mod->processes.size() > 0) { - log("Skipping module %s as it contains processes.\n", log_id(mod)); - continue; - } - - // Create an assign_map for the module - AbcSigMap assign_map; - assign_map.set(mod); - - // Create an FfInitVals and use it for all ABC runs. FfInitVals only cares about - // wires with the ID::init attribute and we don't add or remove any such wires - // in this pass. - FfInitVals initvals; - initvals.set(&assign_map, mod); - - // Populate assign_map - for (auto wire : mod->wires()) - if (wire->port_id > 0 || wire->get_bool_attribute(ID::keep)) - assign_map.addVal(SigSpec(wire), AbcSigVal(true)); - - // Populate assign_map with cell connections - std::vector cells = mod->selected_cells(); - assign_cell_connection_ports(mod, {&cells}, assign_map); - - // Create a map of all signals and their corresponding src attrs - SigMap sigmap(mod); - dict sig2src; - for (auto wire : mod->wires()) - if (wire->port_input) - for (auto bit : sigmap(wire)) - sig2src[bit] = wire->get_src_attribute(); - for (auto cell : mod->cells()) - for (auto &conn : cell->connections()) - if (cell->output(conn.first)) - for (auto bit : sigmap(conn.second)) { - if (GetSize(cell->attributes) > 0) - sig2src[bit] = cell->get_src_attribute(); - else - sig2src[bit] = bit.wire->get_src_attribute(); - } - - // Prepare modules for ABC runs and set up process pool - AbcModuleState *state = new AbcModuleState(config, initvals, 0); - state->prepare_module(design, mod, assign_map, cells, dff_mode, clk_str); - - // Store collateral for use in later stages - module_assign_maps[mod] = assign_map; - module_sigmaps[mod] = sigmap; - module_sig2srcs[mod] = sig2src; - module_initvals[mod] = initvals; - module_states[mod] = state; - } - - // STAGE 2: Run ABC in parallel - // Reserve cores for main thread and other work, and don't create more worker - // threads than ABC runs (unless explicitly configured). - int num_modules = GetSize(design->selected_modules()); - int max_threads = config.max_threads; - if (max_threads < 0) { - // Auto mode: use number of modules as max threads - max_threads = num_modules; - } - if (max_threads <= 1) { - // Just do everything on the main thread. - max_threads = 0; - } -#ifdef YOSYS_LINK_ABC - // ABC doesn't support multithreaded calls so don't call it off the main thread. - max_threads = 0; -#endif - int num_worker_threads = ThreadPool::pool_size(config.reserved_cores, max_threads); - ConcurrentQueue work_queue(num_worker_threads); - ConcurrentQueue work_finished_queue; - ConcurrentStack process_pool; - ThreadPool worker_threads(num_worker_threads, [&](int){ - while (std::optional work = work_queue.pop_front()) { - // Only the `run_abc` component is safe to touch here! - (*work)->run_abc.run(process_pool); - work_finished_queue.push_back(*work); - } - }); - int work_finished_count = 0; - for (auto mod : design->selected_modules()) { - // Do not allow modules with processes - if (mod->processes.size() > 0) continue; - - // Log - log("Sending module %s to abc...\n", log_id(mod)); - log_flush(); - - // Get the state for the module - AbcModuleState *state = module_states.at(mod); - - // Make sure we process the results in the order we expect. When we can - // process results before the next ABC run, do so, to keep memory usage low(er). - while (std::optional work = work_finished_queue.try_pop_front()) { - ++work_finished_count; - } - if (num_worker_threads > 0) { - work_queue.push_back(state); - } else { - // Just run everything on the main thread. - state->run_abc.run(process_pool); - work_finished_queue.push_back(state); - } - } - work_queue.close(); - while (work_finished_count < num_modules) { - std::optional work = work_finished_queue.pop_front(); - (*work)->run_abc.logs.flush(); - log_flush(); - ++work_finished_count; - log("Completed abc on module %d/%d\n", work_finished_count, num_modules); - log_flush(); - } - - // STAGE 3: Extract results and replace original netlist (sequential) - for (auto mod : design->selected_modules()) - { - // Do not allow modules with processes - if (mod->processes.size() > 0) continue; - - // Extraction - AbcModuleState *state = module_states.at(mod); - SigMap sigmap = module_sigmaps.at(mod); - dict sig2src = module_sig2srcs.at(mod); - AbcSigMap assign_map = module_assign_maps.at(mod); - state->extract(assign_map, sig2src, sigmap, design, mod); - delete state; - } - - // STAGE 4: Cleanup - goto cleanup; - } - - // DFF/clock-domain mode for (auto mod : design->selected_modules()) { if (mod->processes.size() > 0) { @@ -2643,22 +2376,17 @@ struct AbcPass : public Pass { if (wire->port_id > 0 || wire->get_bool_attribute(ID::keep)) assign_map.addVal(SigSpec(wire), AbcSigVal(true)); - // SILIMATE: Create a map of all signals and their corresponding src attr - SigMap sigmap(mod); - dict sig2src; - for (auto wire : mod->wires()) - if (wire->port_input) - for (auto bit : sigmap(wire)) - sig2src[bit] = wire->get_src_attribute(); - for (auto cell : mod->cells()) - for (auto &conn : cell->connections()) - if (cell->output(conn.first)) - for (auto bit : sigmap(conn.second)) { - if (GetSize(cell->attributes) > 0) - sig2src[bit] = cell->get_src_attribute(); - else - sig2src[bit] = bit.wire->get_src_attribute(); - } + if (!dff_mode || !clk_str.empty()) { + std::vector cells = mod->selected_cells(); + assign_cell_connection_ports(mod, {&cells}, assign_map); + + AbcModuleState state(config, initvals, 0); + state.prepare_module(design, mod, assign_map, cells, dff_mode, clk_str); + ConcurrentStack process_pool; + state.run_abc.run(process_pool); + state.extract(assign_map, design, mod); + continue; + } CellTypes ct(design); @@ -2851,7 +2579,7 @@ struct AbcPass : public Pass { ++work_finished_count; } while (work_finished_by_index[next_state_index_to_process] != nullptr) { - work_finished_by_index[next_state_index_to_process]->extract(assign_map, sig2src, sigmap, design, mod); + work_finished_by_index[next_state_index_to_process]->extract(assign_map, design, mod); work_finished_by_index[next_state_index_to_process] = nullptr; ++next_state_index_to_process; } @@ -2881,13 +2609,12 @@ struct AbcPass : public Pass { ++work_finished_count; } while (next_state_index_to_process < GetSize(work_finished_by_index)) { - work_finished_by_index[next_state_index_to_process]->extract(assign_map, sig2src, sigmap, design, mod); + work_finished_by_index[next_state_index_to_process]->extract(assign_map, design, mod); work_finished_by_index[next_state_index_to_process] = nullptr; ++next_state_index_to_process; } } - cleanup: if (config.cleanup) { log("Removing global temp directory.\n"); remove_directory(config.global_tempdir_name); @@ -2897,4 +2624,4 @@ struct AbcPass : public Pass { } } AbcPass; -PRIVATE_NAMESPACE_END +PRIVATE_NAMESPACE_END \ No newline at end of file From 48e7b5a16786ef44f172420dec0d589df5116545 Mon Sep 17 00:00:00 2001 From: Akash Levy Date: Wed, 4 Feb 2026 04:33:19 -0800 Subject: [PATCH 21/21] Let's go back to a simpler time for abc... --- passes/techmap/abc.cc | 278 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 249 insertions(+), 29 deletions(-) diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 915899b7d..9b22c38f0 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -136,6 +136,7 @@ struct AbcConfig bool fast_mode = false; bool show_tempdir = false; bool sop_mode = false; + bool word_mode = false; bool abc_dress = false; bool map_mux4 = false; bool map_mux8 = false; @@ -143,6 +144,8 @@ struct AbcConfig bool markgroups = false; pool enabled_gates; bool cmos_cost = false; + int max_threads = -1; // -1 means auto (use number of modules) + int reserved_cores = 4; // cores reserved for main thread and other work }; struct AbcSigVal { @@ -313,7 +316,7 @@ struct AbcModuleState { void handle_loops(AbcSigMap &assign_map, RTLIL::Module *module); void prepare_module(RTLIL::Design *design, RTLIL::Module *module, AbcSigMap &assign_map, const std::vector &cells, bool dff_mode, std::string clk_str); - void extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL::Module *module); + void extract(AbcSigMap &assign_map, dict &sig2src, SigMap &orig_sigmap, RTLIL::Design *design, RTLIL::Module *module); void finish(); }; @@ -578,12 +581,8 @@ bool AbcModuleState::extract_cell(const AbcSigMap &assign_map, RTLIL::Module *mo std::string AbcModuleState::remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire) { std::string abc_sname = abc_name.substr(1); - bool isnew = false; - if (abc_sname.compare(0, 4, "new_") == 0) - { + if (abc_sname.compare(0, 9, "new_ys__n") == 0) abc_sname.erase(0, 4); - isnew = true; - } if (abc_sname.compare(0, 5, "ys__n") == 0) { abc_sname.erase(0, 5); @@ -598,11 +597,9 @@ std::string AbcModuleState::remap_name(RTLIL::IdString abc_name, RTLIL::Wire **o const auto &bit = signal_bits.at(sid); if (bit.wire != nullptr) { - std::string s = stringf("$abc$%d$%s", map_autoidx, bit.wire->name.c_str()+1); + std::string s = stringf("\\%s_ix%d", bit.wire->name.c_str()+1, map_autoidx); // SILIMATE: Improve the naming if (bit.wire->width != 1) s += stringf("[%d]", bit.offset); - if (isnew) - s += "_new"; s += postfix; if (orig_wire != nullptr) *orig_wire = bit.wire; @@ -611,7 +608,7 @@ std::string AbcModuleState::remap_name(RTLIL::IdString abc_name, RTLIL::Wire **o } } } - return stringf("$abc$%d$%s", map_autoidx, abc_name.substr(1)); + return stringf("\\%s_ix%d", abc_sname.c_str(), map_autoidx); // SILIMATE: Improve the naming } void AbcModuleState::dump_loop_graph(FILE *f, int &nr, dict> &edges, pool &workpool, std::vector &in_counts) @@ -1371,7 +1368,7 @@ void RunAbcState::run(ConcurrentStack &process_pool) did_run = true; return; } - log("Don't call ABC as there is nothing to map.\n"); + logs.log("Don't call ABC as there is nothing to map.\n"); } void emit_global_input_files(const AbcConfig &config) @@ -1433,7 +1430,7 @@ void emit_global_input_files(const AbcConfig &config) } } -void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL::Module *module) +void AbcModuleState::extract(AbcSigMap &assign_map, dict &sig2src, SigMap &orig_sigmap, RTLIL::Design *design, RTLIL::Module *module) { log_push(); log_header(design, "Executed ABC.\n"); @@ -1459,22 +1456,39 @@ void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL RTLIL::Module *mapped_mod = mapped_design->module(ID(netlist)); if (mapped_mod == nullptr) log_error("ABC output file does not contain a module `netlist'.\n"); + SigMap mapped_sigmap(mapped_mod); // SILIMATE: Move mapped sigmap definition here bool markgroups = run_abc.config.markgroups; for (auto w : mapped_mod->wires()) { RTLIL::Wire *orig_wire = nullptr; RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire)); if (orig_wire != nullptr && orig_wire->attributes.count(ID::src)) wire->attributes[ID::src] = orig_wire->attributes[ID::src]; + + // SILIMATE: Apply src attribute to the wire from the original wire + if (orig_wire != nullptr) { + if (sig2src.count(orig_sigmap(orig_wire))) { + wire->set_src_attribute(sig2src[orig_sigmap(orig_wire)]); + sig2src[mapped_sigmap(wire)] = wire->get_src_attribute(); + log_debug("Matched wire %s to driver attributes:\n", orig_wire->name.c_str()); + } else { + log_debug("No driver attributes found for wire %s\n", orig_wire->name.c_str()); + } + } + if (markgroups) wire->attributes[ID::abcgroup] = map_autoidx; design->select(module, wire); } - SigMap mapped_sigmap(mapped_mod); FfInitVals mapped_initvals(&mapped_sigmap, mapped_mod); dict cell_stats; for (auto c : mapped_mod->cells()) { + // SILIMATE: set output port to either Y or Q depending on the cell's ports and apply src attribute to the driver cell + Wire *out_wire = c->getPort((c->hasPort(ID::Y)) ? ID::Y : ID::Q).as_wire(); + Wire *remapped_out_wire = module->wire(remap_name(out_wire->name)); + std::string src_attribute = sig2src[remapped_out_wire]; + if (builtin_lib) { cell_stats[RTLIL::unescape_id(c->type)]++; @@ -1496,32 +1510,57 @@ void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL continue; } if (c->type == ID(NOT)) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_NOT_)); + // SILIMATE: use word-level primitives + RTLIL::Cell *cell = module->addCell(remap_name(c->name), run_abc.config.word_mode ? ID($not) : ID($_NOT_)); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; for (auto name : {ID::A, ID::Y}) { RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); cell->setPort(name, module->wire(remapped_name)); } + cell->set_src_attribute(src_attribute); // SILIMATE: set src attribute from wire + cell->fixup_parameters(); // SILIMATE: fix up parameters design->select(module, cell); continue; } if (c->type.in(ID(AND), ID(OR), ID(XOR), ID(NAND), ID(NOR), ID(XNOR), ID(ANDNOT), ID(ORNOT))) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); + // SILIMATE: use word-level primitives + std::string cell_type; + if (c->type == ID(AND) && run_abc.config.word_mode) + cell_type = "$and"; + else if (c->type == ID(OR) && run_abc.config.word_mode) + cell_type = "$or"; + else if (c->type == ID(XOR) && run_abc.config.word_mode) + cell_type = "$xor"; + else + cell_type = stringf("$_%s_", c->type.c_str()+1); + + RTLIL::Cell *cell = module->addCell(remap_name(c->name), cell_type); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; for (auto name : {ID::A, ID::B, ID::Y}) { RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); cell->setPort(name, module->wire(remapped_name)); } + cell->set_src_attribute(src_attribute); // SILIMATE: set src attribute from wire + cell->fixup_parameters(); // SILIMATE: fix up parameters design->select(module, cell); continue; } if (c->type.in(ID(MUX), ID(NMUX))) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); + // SILIMATE: use word-level primitives + std::string cell_type; + if (c->type == ID(MUX) && run_abc.config.word_mode) + cell_type = "$mux"; + else + cell_type = stringf("$_%s_", c->type.c_str()+1); + + RTLIL::Cell *cell = module->addCell(remap_name(c->name), cell_type); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; for (auto name : {ID::A, ID::B, ID::S, ID::Y}) { RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); cell->setPort(name, module->wire(remapped_name)); } + cell->set_src_attribute(src_attribute); // SILIMATE: set src attribute from wire + cell->fixup_parameters(); // SILIMATE: fix up parameters design->select(module, cell); continue; } @@ -1959,6 +1998,16 @@ struct AbcPass : public Pass { log(" preserve naming by an equivalence check between the original and\n"); log(" post-ABC netlists (experimental).\n"); log("\n"); + log(" -max_threads \n"); + log(" maximum number of worker threads for parallel ABC runs. Default is -1,\n"); + log(" which means auto (use number of modules). Set to 0 to disable parallel\n"); + log(" execution and run everything on the main thread.\n"); + log("\n"); + log(" -reserved_cores \n"); + log(" number of CPU cores to reserve for the main thread and other work.\n"); + log(" Default is 4. The actual number of worker threads used is:\n"); + log(" min(hardware_threads - reserved_cores, max_threads)\n"); + log("\n"); log("When no target cell library is specified the Yosys standard cell library is\n"); log("loaded into ABC before the ABC script is executed.\n"); log("\n"); @@ -2002,6 +2051,7 @@ struct AbcPass : public Pass { lut_arg = design->scratchpad_get_string("abc.lut", lut_arg); luts_arg = design->scratchpad_get_string("abc.luts", luts_arg); config.sop_mode = design->scratchpad_get_bool("abc.sop", false); + config.word_mode = design->scratchpad_get_bool("abc.word", false); config.map_mux4 = design->scratchpad_get_bool("abc.mux4", false); config.map_mux8 = design->scratchpad_get_bool("abc.mux8", false); config.map_mux16 = design->scratchpad_get_bool("abc.mux16", false); @@ -2019,6 +2069,8 @@ struct AbcPass : public Pass { config.cleanup = !design->scratchpad_get_bool("abc.nocleanup", false); config.show_tempdir = design->scratchpad_get_bool("abc.showtmp", false); config.markgroups = design->scratchpad_get_bool("abc.markgroups", false); + config.max_threads = design->scratchpad_get_int("abc.max_threads", -1); + config.reserved_cores = design->scratchpad_get_int("abc.reserved_cores", 4); if (config.cleanup) config.global_tempdir_name = get_base_tmpdir() + "/"; @@ -2097,6 +2149,10 @@ struct AbcPass : public Pass { config.sop_mode = true; continue; } + if (arg == "-word") { + config.word_mode = true; + continue; + } if (arg == "-mux4") { config.map_mux4 = true; continue; @@ -2150,6 +2206,14 @@ struct AbcPass : public Pass { config.markgroups = true; continue; } + if (arg == "-max_threads" && argidx+1 < args.size()) { + config.max_threads = atoi(args[++argidx].c_str()); + continue; + } + if (arg == "-reserved_cores" && argidx+1 < args.size()) { + config.reserved_cores = atoi(args[++argidx].c_str()); + continue; + } break; } extra_args(args, argidx, design); @@ -2357,6 +2421,156 @@ struct AbcPass : public Pass { emit_global_input_files(config); + // Process non-DFF/non-clock-domain mode in stages + if (!dff_mode || !clk_str.empty()) { + // Maps for collateral storage across stages + dict module_assign_maps; + dict module_sigmaps; + dict> module_sig2srcs; + dict module_initvals; + dict module_states; + + // STAGE 1: Compute assign_maps, sig2src maps, and initvals for all modules + // Then prepare for ABC runs (sequential) + for (auto mod : design->selected_modules()) + { + // Do not allow modules with processes + if (mod->processes.size() > 0) { + log("Skipping module %s as it contains processes.\n", log_id(mod)); + continue; + } + + // Create an assign_map for the module + AbcSigMap assign_map; + assign_map.set(mod); + + // Create an FfInitVals and use it for all ABC runs. FfInitVals only cares about + // wires with the ID::init attribute and we don't add or remove any such wires + // in this pass. + FfInitVals initvals; + initvals.set(&assign_map, mod); + + // Populate assign_map + for (auto wire : mod->wires()) + if (wire->port_id > 0 || wire->get_bool_attribute(ID::keep)) + assign_map.addVal(SigSpec(wire), AbcSigVal(true)); + + // Populate assign_map with cell connections + std::vector cells = mod->selected_cells(); + assign_cell_connection_ports(mod, {&cells}, assign_map); + + // Create a map of all signals and their corresponding src attrs + SigMap sigmap(mod); + dict sig2src; + for (auto wire : mod->wires()) + if (wire->port_input) + for (auto bit : sigmap(wire)) + sig2src[bit] = wire->get_src_attribute(); + for (auto cell : mod->cells()) + for (auto &conn : cell->connections()) + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) { + if (GetSize(cell->attributes) > 0) + sig2src[bit] = cell->get_src_attribute(); + else + sig2src[bit] = bit.wire->get_src_attribute(); + } + + // Prepare modules for ABC runs and set up process pool + AbcModuleState *state = new AbcModuleState(config, initvals, 0); + state->prepare_module(design, mod, assign_map, cells, dff_mode, clk_str); + + // Store collateral for use in later stages + module_assign_maps[mod] = assign_map; + module_sigmaps[mod] = sigmap; + module_sig2srcs[mod] = sig2src; + module_initvals[mod] = initvals; + module_states[mod] = state; + } + + // STAGE 2: Run ABC in parallel + // Reserve cores for main thread and other work, and don't create more worker + // threads than ABC runs (unless explicitly configured). + int num_modules = GetSize(design->selected_modules()); + int max_threads = config.max_threads; + if (max_threads < 0) { + // Auto mode: use number of modules as max threads + max_threads = num_modules; + } + if (max_threads <= 1) { + // Just do everything on the main thread. + max_threads = 0; + } +#ifdef YOSYS_LINK_ABC + // ABC doesn't support multithreaded calls so don't call it off the main thread. + max_threads = 0; +#endif + int num_worker_threads = ThreadPool::pool_size(config.reserved_cores, max_threads); + ConcurrentQueue work_queue(num_worker_threads); + ConcurrentQueue work_finished_queue; + ConcurrentStack process_pool; + ThreadPool worker_threads(num_worker_threads, [&](int){ + while (std::optional work = work_queue.pop_front()) { + // Only the `run_abc` component is safe to touch here! + (*work)->run_abc.run(process_pool); + work_finished_queue.push_back(*work); + } + }); + int work_finished_count = 0; + for (auto mod : design->selected_modules()) { + // Do not allow modules with processes + if (mod->processes.size() > 0) continue; + + // Log + log("Sending module %s to abc...\n", log_id(mod)); + log_flush(); + + // Get the state for the module + AbcModuleState *state = module_states.at(mod); + + // Make sure we process the results in the order we expect. When we can + // process results before the next ABC run, do so, to keep memory usage low(er). + while (std::optional work = work_finished_queue.try_pop_front()) { + ++work_finished_count; + } + if (num_worker_threads > 0) { + work_queue.push_back(state); + } else { + // Just run everything on the main thread. + state->run_abc.run(process_pool); + work_finished_queue.push_back(state); + } + } + work_queue.close(); + while (work_finished_count < num_modules) { + std::optional work = work_finished_queue.pop_front(); + (*work)->run_abc.logs.flush(); + log_flush(); + ++work_finished_count; + log("Completed abc on module %d/%d\n", work_finished_count, num_modules); + log_flush(); + } + + // STAGE 3: Extract results and replace original netlist (sequential) + for (auto mod : design->selected_modules()) + { + // Do not allow modules with processes + if (mod->processes.size() > 0) continue; + + // Extraction + AbcModuleState *state = module_states.at(mod); + SigMap sigmap = module_sigmaps.at(mod); + dict sig2src = module_sig2srcs.at(mod); + AbcSigMap assign_map = module_assign_maps.at(mod); + state->extract(assign_map, sig2src, sigmap, design, mod); + delete state; + } + + // STAGE 4: Cleanup + goto cleanup; + } + + // DFF/clock-domain mode for (auto mod : design->selected_modules()) { if (mod->processes.size() > 0) { @@ -2376,17 +2590,22 @@ struct AbcPass : public Pass { if (wire->port_id > 0 || wire->get_bool_attribute(ID::keep)) assign_map.addVal(SigSpec(wire), AbcSigVal(true)); - if (!dff_mode || !clk_str.empty()) { - std::vector cells = mod->selected_cells(); - assign_cell_connection_ports(mod, {&cells}, assign_map); - - AbcModuleState state(config, initvals, 0); - state.prepare_module(design, mod, assign_map, cells, dff_mode, clk_str); - ConcurrentStack process_pool; - state.run_abc.run(process_pool); - state.extract(assign_map, design, mod); - continue; - } + // SILIMATE: Create a map of all signals and their corresponding src attr + SigMap sigmap(mod); + dict sig2src; + for (auto wire : mod->wires()) + if (wire->port_input) + for (auto bit : sigmap(wire)) + sig2src[bit] = wire->get_src_attribute(); + for (auto cell : mod->cells()) + for (auto &conn : cell->connections()) + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) { + if (GetSize(cell->attributes) > 0) + sig2src[bit] = cell->get_src_attribute(); + else + sig2src[bit] = bit.wire->get_src_attribute(); + } CellTypes ct(design); @@ -2579,7 +2798,7 @@ struct AbcPass : public Pass { ++work_finished_count; } while (work_finished_by_index[next_state_index_to_process] != nullptr) { - work_finished_by_index[next_state_index_to_process]->extract(assign_map, design, mod); + work_finished_by_index[next_state_index_to_process]->extract(assign_map, sig2src, sigmap, design, mod); work_finished_by_index[next_state_index_to_process] = nullptr; ++next_state_index_to_process; } @@ -2609,12 +2828,13 @@ struct AbcPass : public Pass { ++work_finished_count; } while (next_state_index_to_process < GetSize(work_finished_by_index)) { - work_finished_by_index[next_state_index_to_process]->extract(assign_map, design, mod); + work_finished_by_index[next_state_index_to_process]->extract(assign_map, sig2src, sigmap, design, mod); work_finished_by_index[next_state_index_to_process] = nullptr; ++next_state_index_to_process; } } + cleanup: if (config.cleanup) { log("Removing global temp directory.\n"); remove_directory(config.global_tempdir_name);