From fb864e91ee7ba47cc52a38024c353acec156b78a Mon Sep 17 00:00:00 2001 From: Natalia Date: Wed, 14 Jan 2026 17:35:45 -0800 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] 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")