diff --git a/CHANGELOG b/CHANGELOG index 73c1606da..e345a8514 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,9 +2,30 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.61 .. Yosys 0.62-dev +Yosys 0.62 .. Yosys 0.63-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 -------------------------- * Various diff --git a/Makefile b/Makefile index f17128daf..c3ebc7378 100644 --- a/Makefile +++ b/Makefile @@ -177,7 +177,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.61+112 +YOSYS_VER := 0.62+0 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'.' -f3) @@ -202,7 +202,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) OPTFLAGS="-g -O3" 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' diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 28cc850a9..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(); @@ -3864,14 +3861,17 @@ struct VerificPass : public Pass { { #ifdef YOSYSHQ_VERIFIC_EXTENSIONS 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 #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT unsigned verilog_mode = veri_file::SYSTEM_VERILOG; #endif #ifdef VERIFIC_VHDL_SUPPORT unsigned vhdl_mode = vhdl_file::UNDEFINED; +#endif #endif const char* filename = nullptr; diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 43e4930d1..d27eabc25 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1612,12 +1612,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 74675b079..ef9a077f3 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -2039,9 +2039,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); } - // SILIMATE ADDED TO IMPROVE PYOSYS API - 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/passes/sat/sim.cc b/passes/sat/sim.cc index c029cb2df..a0cfee542 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; } @@ -2504,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); @@ -2527,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: "; @@ -2577,10 +2577,8 @@ struct AnnotateActivity : public OutputWriter { totalActivity += activity; activity_str += std::to_string(activity) + " "; } - if (debug) { - std::cout << activity_str; - std::cout << "\n"; - std::cout << " DUTY: "; + if (worker->debug) { + log_debug(" ACKT: %s", activity_str.c_str()); } std::string duty_str; for (uint32_t i = 0; i < (uint32_t)size; i++) { @@ -2589,9 +2587,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); 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/passes/techmap/abc.cc b/passes/techmap/abc.cc index 5d1967f15..66f6e76db 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -2873,4 +2873,4 @@ struct AbcPass : public Pass { } } AbcPass; -PRIVATE_NAMESPACE_END +PRIVATE_NAMESPACE_END \ No newline at end of file diff --git a/pyosys/generator.py b/pyosys/generator.py index 14c9421d9..d44ed9656 100644 --- a/pyosys/generator.py +++ b/pyosys/generator.py @@ -708,6 +708,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(&s, cmd); })', + file=self.f, + ) + print( + '\t\t\t.def("run_pass", [](Design &s, std::string cmd) { Pass::call(&s, cmd); })', + 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..f0013577d --- /dev/null +++ b/tests/pyosys/test_design_run_pass.py @@ -0,0 +1,20 @@ +from pathlib import Path +from pyosys import libyosys as ys + +__file_dir__ = Path(__file__).absolute().parent +add_sub = __file_dir__.parent / "arch" / "common" / "add_sub.v" + +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") 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 <