From e166dd4475e8be421858d06a45138ff45d55f4fc Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 9 Jan 2026 18:43:15 +0100 Subject: [PATCH 01/19] add $priority cell --- kernel/calc.cc | 19 +++++++++++++++++++ kernel/celltypes.h | 7 ++++++- kernel/constids.inc | 1 + kernel/rtlil.cc | 17 ++++++++++++++++- kernel/rtlil.h | 3 +++ kernel/satgen.cc | 33 +++++++++++++++++++++++++++++++++ passes/tests/test_cell.cc | 16 ++++++++++++++++ techlibs/common/simlib.v | 16 ++++++++++++++++ techlibs/common/techmap.v | 26 ++++++++++++++++++++++++++ 9 files changed, 136 insertions(+), 2 deletions(-) diff --git a/kernel/calc.cc b/kernel/calc.cc index 9b0885db9..5ddb230b2 100644 --- a/kernel/calc.cc +++ b/kernel/calc.cc @@ -658,6 +658,25 @@ RTLIL::Const RTLIL::const_bmux(const RTLIL::Const &arg1, const RTLIL::Const &arg return t; } +RTLIL::Const RTLIL::const_priority(const RTLIL::Const &arg) +{ + std::vector t; + std::optional first_non_zero = std::nullopt; + for (int i = 0; i < GetSize(arg); i++) + { + RTLIL::State s = arg.at(i); + if (first_non_zero && s != State::Sx) { + t.push_back(*first_non_zero == State::S1 ? State::S0 : *first_non_zero); + } else { + t.push_back(s); + } + if ((!first_non_zero && s != State::S0) || s == State::Sx) { + first_non_zero = s; + } + } + return t; +} + RTLIL::Const RTLIL::const_demux(const RTLIL::Const &arg1, const RTLIL::Const &arg2) { int width = GetSize(arg1); diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 34b013dd9..d3aa4399c 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -118,7 +118,7 @@ struct CellTypes void setup_internals_eval() { std::vector unary_ops = { - ID($not), ID($pos), ID($buf), ID($neg), + ID($not), ID($pos), ID($buf), ID($neg), ID($priority), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($logic_not), ID($slice), ID($lut), ID($sop) }; @@ -509,6 +509,11 @@ struct CellTypes return default_ret; } + if (cell->type == ID($priority)) + { + return const_priority(arg1); + } + bool signed_a = cell->parameters.count(ID::A_SIGNED) > 0 && cell->parameters[ID::A_SIGNED].as_bool(); bool signed_b = cell->parameters.count(ID::B_SIGNED) > 0 && cell->parameters[ID::B_SIGNED].as_bool(); int result_len = cell->parameters.count(ID::Y_WIDTH) > 0 ? cell->parameters[ID::Y_WIDTH].as_int() : -1; diff --git a/kernel/constids.inc b/kernel/constids.inc index c99aa788d..315944b9d 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -259,6 +259,7 @@ X($pmux) X($pos) X($pow) X($print) +X($priority) X($recrem) X($reduce_and) X($reduce_bool) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 0103cabfb..de54c4bd8 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -2654,6 +2654,13 @@ namespace { check_expected(); return; } + if (cell->type.in(ID($priority))) { + param(ID::WIDTH); + port(ID::A, param(ID::WIDTH)); + port(ID::Y, param(ID::WIDTH)); + check_expected(); + return; + } /* * Checklist for adding internal cell types * ======================================== @@ -3969,6 +3976,14 @@ RTLIL::Cell* RTLIL::Module::addDlatchsr(RTLIL::IdString name, const RTLIL::SigSp cell->set_src_attribute(src); return cell; } +RTLIL::Cell* RTLIL::Module::addPriority(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, ID($priority)); + cell->setPort(ID::A, sig_a); + cell->setPort(ID::Y, sig_y); + cell->set_src_attribute(src); + return cell; +} RTLIL::Cell* RTLIL::Module::addSrGate(RTLIL::IdString name, const RTLIL::SigSpec &sig_set, const RTLIL::SigSpec &sig_clr, const RTLIL::SigSpec &sig_q, bool set_polarity, bool clr_polarity, const std::string &src) @@ -4546,7 +4561,7 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) return; } - if (type == ID($lut) || type == ID($sop)) { + if (type == ID($lut) || type == ID($sop) || type == ID($priority)) { parameters[ID::WIDTH] = GetSize(connections_[ID::A]); return; } diff --git a/kernel/rtlil.h b/kernel/rtlil.h index fe280c965..3dddfd7f0 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -848,6 +848,7 @@ namespace RTLIL { RTLIL::Const const_pmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3); RTLIL::Const const_bmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2); RTLIL::Const const_demux (const RTLIL::Const &arg1, const RTLIL::Const &arg2); + RTLIL::Const const_priority (const RTLIL::Const &arg); RTLIL::Const const_bweqx (const RTLIL::Const &arg1, const RTLIL::Const &arg2); RTLIL::Const const_bwmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3); @@ -2262,6 +2263,8 @@ public: RTLIL::Cell* addAdlatch (RTLIL::IdString name, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_arst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const arst_value, bool en_polarity = true, bool arst_polarity = true, const std::string &src = ""); RTLIL::Cell* addDlatchsr (RTLIL::IdString name, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_set, const RTLIL::SigSpec &sig_clr, RTLIL::SigSpec sig_d, const RTLIL::SigSpec &sig_q, bool en_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = ""); + RTLIL::Cell* addPriority (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, const std::string &src = ""); + RTLIL::Cell* addBufGate (RTLIL::IdString name, const RTLIL::SigBit &sig_a, const RTLIL::SigBit &sig_y, const std::string &src = ""); RTLIL::Cell* addNotGate (RTLIL::IdString name, const RTLIL::SigBit &sig_a, const RTLIL::SigBit &sig_y, const std::string &src = ""); RTLIL::Cell* addAndGate (RTLIL::IdString name, const RTLIL::SigBit &sig_a, const RTLIL::SigBit &sig_b, const RTLIL::SigBit &sig_y, const std::string &src = ""); diff --git a/kernel/satgen.cc b/kernel/satgen.cc index f2c1e00c2..2e5b27530 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -430,6 +430,39 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) return true; } + if (cell->type == ID($priority)) + { + std::vector a = importDefSigSpec(cell->getPort(ID::A), timestep); + std::vector y = importDefSigSpec(cell->getPort(ID::Y), timestep); + std::vector yy = model_undef ? ez->vec_var(y.size()) : y; + + int tmp; + if (a.size()) { + tmp = a[0]; + ez->assume(ez->IFF(yy[0], a[0])); + } + for (size_t i = 1; i < a.size(); i++) { + ez->assume(ez->IFF(yy[i], ez->AND(a[i], ez->NOT(tmp)))); + tmp = ez->OR(tmp, a[i]); + } + if (model_undef) { + std::vector undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep); + std::vector undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep); + + if (a.size()) { + tmp = undef_a[0]; + ez->assume(ez->IFF(undef_y[0], undef_a[0])); + } + for (size_t i = 1; i < a.size(); i++) { + tmp = ez->OR(tmp, undef_a[i]); + ez->assume(ez->IFF(undef_y[i], tmp)); + } + undefGating(y, yy, undef_y); + } + + return true; + } + if (cell->type.in(ID($pos), ID($buf), ID($neg))) { std::vector a = importDefSigSpec(cell->getPort(ID::A), timestep); diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index 4d28e659b..a05d9cb16 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -136,6 +136,21 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce cell->setPort(ID::Y, wire); } + if (cell_type == ID($priority)) + { + int width = 1 + xorshift32(8 * bloat_factor); + + wire = module->addWire(ID::A); + wire->width = width; + wire->port_input = true; + cell->setPort(ID::A, wire); + + wire = module->addWire(ID::Y); + wire->width = width; + wire->port_output = true; + cell->setPort(ID::Y, wire); + } + if (cell_type == ID($fa)) { int width = 1 + xorshift32(8 * bloat_factor); @@ -1039,6 +1054,7 @@ struct TestCellPass : public Pass { cell_types[ID($mux)] = "*"; cell_types[ID($bmux)] = "*"; cell_types[ID($demux)] = "*"; + cell_types[ID($priority)] = "*"; // $pmux doesn't work in sat, and is not supported with 'techmap -assert' or // '-simlib' if (nosat && techmap_cmd.compare("aigmap") == 0) diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index e0fb9fbfa..fcdbab555 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -3250,3 +3250,19 @@ parameter WIDTH = 0; inout [WIDTH-1:0] Y; endmodule + +// -------------------------------------------------------- +//- +//- $priority (A, Y) +//* group unary +//- +//- Priority operator. An output bit is set if the input bit at the same index is set and no lower index input bit is set. +//- +module \$priority (A, Y); +parameter WIDTH = 8; +input [WIDTH-1:0] A; +output [WIDTH-1:0] Y; + +assign Y = A & (~A + 1); + +endmodule diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v index c3364e628..6eb9d01da 100644 --- a/techlibs/common/techmap.v +++ b/techlibs/common/techmap.v @@ -679,3 +679,29 @@ parameter WIDTH = 0; inout [WIDTH-1:0] Y; // This cell is just a maker, so we leave Y undriven endmodule + +(* techmap_celltype = "$priority" *) +module \$priority (A, Y); + parameter WIDTH = 3; + + (* force_downto *) + input [WIDTH-1:0] A; + (* force_downto *) + output [WIDTH-1:0] Y; + + (* force_downto *) + wire [WIDTH-1:0] tmp; + + genvar i; + generate + if (WIDTH > 0) begin + assign tmp[0] = A[0]; + assign Y[0] = A[0]; + end + for (i = 1; i < WIDTH; i = i + 1) begin + assign Y[i] = A[i] & ~tmp[i-1]; + assign tmp[i] = tmp[i-1] | A[i]; + end + endgenerate + +endmodule From 7e664834b3ad72721a5747e148ff5000e1eeb0c8 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 9 Jan 2026 21:02:58 +0100 Subject: [PATCH 02/19] add POLARITY parameter to $priority cell --- kernel/calc.cc | 8 +++++--- kernel/celltypes.h | 2 +- kernel/constids.inc | 1 + kernel/rtlil.cc | 1 + kernel/rtlil.h | 2 +- kernel/satgen.cc | 19 ++++++++++++------- passes/tests/test_cell.cc | 6 ++++++ techlibs/common/techmap.v | 16 +++++++++++----- 8 files changed, 38 insertions(+), 17 deletions(-) diff --git a/kernel/calc.cc b/kernel/calc.cc index 5ddb230b2..a1ba5f7c8 100644 --- a/kernel/calc.cc +++ b/kernel/calc.cc @@ -658,7 +658,7 @@ RTLIL::Const RTLIL::const_bmux(const RTLIL::Const &arg1, const RTLIL::Const &arg return t; } -RTLIL::Const RTLIL::const_priority(const RTLIL::Const &arg) +RTLIL::Const RTLIL::const_priority(const RTLIL::Const &arg, const RTLIL::Const &polarity) { std::vector t; std::optional first_non_zero = std::nullopt; @@ -666,11 +666,13 @@ RTLIL::Const RTLIL::const_priority(const RTLIL::Const &arg) { RTLIL::State s = arg.at(i); if (first_non_zero && s != State::Sx) { - t.push_back(*first_non_zero == State::S1 ? State::S0 : *first_non_zero); + auto inactive = polarity[i] == State::S0 ? State::S1 : State::S0; + auto val = *first_non_zero == State::Sx ? State::Sx : inactive; + t.push_back(val); } else { t.push_back(s); } - if ((!first_non_zero && s != State::S0) || s == State::Sx) { + if ((!first_non_zero && s == polarity[i]) || s == State::Sx) { first_non_zero = s; } } diff --git a/kernel/celltypes.h b/kernel/celltypes.h index d3aa4399c..8423deadc 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -511,7 +511,7 @@ struct CellTypes if (cell->type == ID($priority)) { - return const_priority(arg1); + return const_priority(arg1, cell->getParam(ID::POLARITY)); } bool signed_a = cell->parameters.count(ID::A_SIGNED) > 0 && cell->parameters[ID::A_SIGNED].as_bool(); diff --git a/kernel/constids.inc b/kernel/constids.inc index 315944b9d..250c8688c 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -615,6 +615,7 @@ X(PATTERN) X(PCIN) X(PIPELINE_16x16_MULT_REG1) X(PIPELINE_16x16_MULT_REG2) +X(POLARITY) X(PORTID) X(PORT_A1_ADDR) X(PORT_A1_CLK) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index de54c4bd8..505c81da5 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -2656,6 +2656,7 @@ namespace { } if (cell->type.in(ID($priority))) { param(ID::WIDTH); + param(ID::POLARITY); port(ID::A, param(ID::WIDTH)); port(ID::Y, param(ID::WIDTH)); check_expected(); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 3dddfd7f0..1e16108da 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -848,7 +848,7 @@ namespace RTLIL { RTLIL::Const const_pmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3); RTLIL::Const const_bmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2); RTLIL::Const const_demux (const RTLIL::Const &arg1, const RTLIL::Const &arg2); - RTLIL::Const const_priority (const RTLIL::Const &arg); + RTLIL::Const const_priority (const RTLIL::Const &arg, const RTLIL::Const &polarity); RTLIL::Const const_bweqx (const RTLIL::Const &arg1, const RTLIL::Const &arg2); RTLIL::Const const_bwmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3); diff --git a/kernel/satgen.cc b/kernel/satgen.cc index 2e5b27530..c608a0c19 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -436,26 +436,31 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector y = importDefSigSpec(cell->getPort(ID::Y), timestep); std::vector yy = model_undef ? ez->vec_var(y.size()) : y; - int tmp; + const Const& polarity = cell->getParam(ID::POLARITY); + + int any_previous_active; if (a.size()) { - tmp = a[0]; + any_previous_active = polarity[0] ? a[0] : ez->NOT(a[0]); ez->assume(ez->IFF(yy[0], a[0])); } for (size_t i = 1; i < a.size(); i++) { - ez->assume(ez->IFF(yy[i], ez->AND(a[i], ez->NOT(tmp)))); - tmp = ez->OR(tmp, a[i]); + int inactive_val = !polarity[i] ? ez->CONST_TRUE : ez->CONST_FALSE; + int active_val = polarity[i] ? ez->CONST_TRUE : ez->CONST_FALSE; + ez->assume(ez->IFF(yy[i], ez->ITE(any_previous_active, inactive_val, a[i]))); + any_previous_active = ez->OR(any_previous_active, ez->IFF(a[i], active_val)); } if (model_undef) { std::vector undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep); std::vector undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep); + int any_previous_undef; if (a.size()) { - tmp = undef_a[0]; + any_previous_undef = undef_a[0]; ez->assume(ez->IFF(undef_y[0], undef_a[0])); } for (size_t i = 1; i < a.size(); i++) { - tmp = ez->OR(tmp, undef_a[i]); - ez->assume(ez->IFF(undef_y[i], tmp)); + any_previous_undef = ez->OR(any_previous_undef, undef_a[i]); + ez->assume(ez->IFF(undef_y[i], any_previous_undef)); } undefGating(y, yy, undef_y); } diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index a05d9cb16..7ae375d96 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -149,6 +149,12 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce wire->width = width; wire->port_output = true; cell->setPort(ID::Y, wire); + + RTLIL::SigSpec polarity; + for (int i = 0; i < width; i++) + polarity.append(xorshift32(2) ? State::S1 : State::S0); + + cell->setParam(ID::POLARITY, polarity.as_const()); } if (cell_type == ID($fa)) diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v index 6eb9d01da..c8f4ec420 100644 --- a/techlibs/common/techmap.v +++ b/techlibs/common/techmap.v @@ -682,7 +682,8 @@ endmodule (* techmap_celltype = "$priority" *) module \$priority (A, Y); - parameter WIDTH = 3; + parameter WIDTH = 0; + parameter POLARITY = 0; (* force_downto *) input [WIDTH-1:0] A; @@ -691,16 +692,21 @@ module \$priority (A, Y); (* force_downto *) wire [WIDTH-1:0] tmp; + (* force_downto *) + wire [WIDTH-1:0] A_active; + wire [WIDTH-1:0] Y_active; + assign A_active = A ^ ~POLARITY; + assign Y = Y_active ^ ~POLARITY; genvar i; generate if (WIDTH > 0) begin - assign tmp[0] = A[0]; - assign Y[0] = A[0]; + assign tmp[0] = A_active[0]; + assign Y_active[0] = A_active[0]; end for (i = 1; i < WIDTH; i = i + 1) begin - assign Y[i] = A[i] & ~tmp[i-1]; - assign tmp[i] = tmp[i-1] | A[i]; + assign Y_active[i] = tmp[i-1] ? 1'b0 : A_active[i]; + assign tmp[i] = tmp[i-1] | A_active[i]; end endgenerate From 92d1a311626ac951ae417d7aa75b19d88743c859 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 16 Jan 2026 12:15:51 +0100 Subject: [PATCH 03/19] proc_dff: refactor --- passes/proc/proc_dff.cc | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 06c740a88..3ac9c9825 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -53,13 +53,19 @@ RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc) return lvalue; } -void gen_dffsr_complex(RTLIL::Module *mod, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, RTLIL::SigSpec clk, bool clk_polarity, - std::vector> &async_rules, RTLIL::Process *proc) +struct DSigs { + RTLIL::SigSpec d; + RTLIL::SigSpec q; + RTLIL::SigSpec clk; +}; +using Rules = std::vector>; +void gen_dffsr_complex(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, + Rules &async_rules, RTLIL::Process *proc) { // A signal should be set/cleared if there is a load trigger that is enabled // such that the load value is 1/0 and it is the highest priority trigger - RTLIL::SigSpec sig_sr_set = RTLIL::SigSpec(0, sig_d.size()); - RTLIL::SigSpec sig_sr_clr = RTLIL::SigSpec(0, sig_d.size()); + RTLIL::SigSpec sig_sr_set = RTLIL::SigSpec(0, sigs.d.size()); + RTLIL::SigSpec sig_sr_clr = RTLIL::SigSpec(0, sigs.d.size()); // Reverse iterate through the rules as the first ones are the highest priority // so need to be at the top of the mux trees @@ -81,7 +87,7 @@ void gen_dffsr_complex(RTLIL::Module *mod, RTLIL::SigSpec sig_d, RTLIL::SigSpec std::stringstream sstr; sstr << "$procdff$" << (autoidx++); - RTLIL::Cell *cell = mod->addDffsr(sstr.str(), clk, sig_sr_set, sig_sr_clr, sig_d, sig_q, clk_polarity); + RTLIL::Cell *cell = mod->addDffsr(sstr.str(), sigs.clk, sig_sr_set, sig_sr_clr, sigs.d, sigs.q, clk_polarity); cell->attributes = proc->attributes; log(" created %s cell `%s' with %s edge clock and multiple level-sensitive resets.\n", @@ -261,7 +267,9 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) if (async_rules.size() > 1) { log_warning("Complex async reset for dff `%s'.\n", log_signal(sig)); - gen_dffsr_complex(mod, insig, sig, sync_edge->signal, sync_edge->type == RTLIL::SyncType::STp, async_rules, proc); + DSigs sigs {insig, sig, sync_edge->signal}; + bool clk_pol = sync_edge->type == RTLIL::SyncType::STp; + gen_dffsr_complex(mod, sigs, clk_pol, async_rules, proc); continue; } From b77a93f8168976e87b19333fccdb8a6a18d161d9 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 16 Jan 2026 16:14:31 +0100 Subject: [PATCH 04/19] proc_dff: emit $dffsr with $priority instead of mux tree --- passes/proc/proc_dff.cc | 124 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 1 deletion(-) diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 3ac9c9825..72f2dbf75 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -17,7 +17,9 @@ * */ +#include "backends/rtlil/rtlil_backend.h" #include "kernel/register.h" +#include "kernel/rtlil.h" #include "kernel/sigtools.h" #include "kernel/consteval.h" #include "kernel/log.h" @@ -59,6 +61,7 @@ struct DSigs { RTLIL::SigSpec clk; }; using Rules = std::vector>; + void gen_dffsr_complex(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, Rules &async_rules, RTLIL::Process *proc) { @@ -94,6 +97,124 @@ void gen_dffsr_complex(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative"); } +void gen_dffsr(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, + Rules &async_rules, ConstEval& ce, RTLIL::Process *proc) +{ + RTLIL::SigSpec sig_sr_set; + RTLIL::SigSpec sig_sr_clr; + + struct BitRule { + SigBit trig; + bool trig_polarity; // true = active high, false = active low + bool effect; // true = set, false = reset + }; + std::vector> bit_rules(sigs.d.size()); + // For checking consistent per-bit set/reset edges and bailing out on inconsistent + std::optional bit_set_pol; + std::optional bit_reset_pol; + + for (auto it = async_rules.cbegin(); it != async_rules.cend(); it++) + { + const auto& [sync_value, rule] = *it; + log_debug("sync_value %s, rule:\n", log_signal(sync_value)); + + for (int i = 0; i < sigs.d.size(); i++) { + log_debug("rule->signal %s\n", log_signal(rule->signal)); + log_debug(rule->signal.size() == 1); + SigSpec value_bit = sync_value[i]; + if (sync_value[i] == sigs.q[i]) { + log_debug("%s is %s\n", log_signal(sync_value[i]), log_signal(sigs.q[i])); + continue; + } + if (!ce.eval(value_bit)) { + log_debug("non-const %s\n", log_signal(sync_value[i])); + gen_dffsr_complex(mod, sigs, clk_polarity, async_rules, proc); + return; + } + bool effect = ce.values_map(value_bit).as_const().as_bool(); + bool trig_pol = rule->type == RTLIL::SyncType::ST1; + while (bit_rules.size() <= (size_t) i) + bit_rules.push_back({}); + bit_rules[i].push_back({rule->signal[0], trig_pol, effect}); + + bool set_inconsistent = effect && bit_set_pol && (*bit_set_pol != trig_pol); + bool reset_inconsistent = !effect && bit_reset_pol && (*bit_reset_pol != trig_pol); + if (set_inconsistent || reset_inconsistent) { + gen_dffsr_complex(mod, sigs, clk_polarity, async_rules, proc); + return; + } + if (effect) { + bit_set_pol = trig_pol; + } else { + bit_reset_pol = trig_pol; + } + } + } + + log_assert(bit_set_pol != std::nullopt); + log_assert(bit_reset_pol != std::nullopt); + + RTLIL::Wire* prioritized = mod->addWire(NEW_ID, sigs.d.size() * async_rules.size()); + RTLIL::Cell* priority = mod->addPriority(NEW_ID, SigSpec(), prioritized); + priority->setParam(ID::WIDTH, sigs.d.size()); + priority->setParam(ID::P_WIDTH, async_rules.size()); + SigSpec priority_in; + std::vector priority_pol; + for (int i = 0; i < sigs.d.size(); i++) { + log_debug("bit %d:\n", i); + SigSpec bit_sets; + SigSpec bit_resets; + for (auto rule : bit_rules[i]) { + log_debug("if %s == %d then set %d\n", log_signal(rule.trig), rule.trig_polarity, rule.effect); + priority_in.append(rule.trig); + priority_pol.push_back(RTLIL::State(rule.trig_polarity)); + if (rule.effect) + bit_sets.append(SigBit(prioritized, priority_in.size() - 1)); + else + bit_resets.append(SigBit(prioritized, priority_in.size() - 1)); + } + std::optional set; + if (bit_sets.size()) { + if (bit_sets.size() == 1) { + set = bit_sets[0]; + } else { + set = mod->addWire(NEW_ID); + // Polarities are consistent, as guaranteed by check prior + (bit_rules[i][0].trig_polarity ? mod->addReduceOr(NEW_ID, bit_sets, *set) : mod->addReduceAnd(NEW_ID, bit_sets, *set))->attributes = proc->attributes; + } + } + std::optional reset; + if (bit_resets.size()) { + if (bit_resets.size() == 1) { + reset = bit_resets[0]; + } else { + reset = mod->addWire(NEW_ID); + (bit_rules[i][0].trig_polarity ? mod->addReduceOr(NEW_ID, bit_resets, *reset) : mod->addReduceAnd(NEW_ID, bit_resets, *reset))->attributes = proc->attributes; + } + } + if (set) + sig_sr_set.append(*set); + else + sig_sr_set.append(*bit_set_pol ? Const(0, 1) : Const(1, 1)); + if (reset) + sig_sr_clr.append(*reset); + else + sig_sr_clr.append(*bit_reset_pol ? Const(0, 1) : Const(1, 1)); + } + priority->setPort(ID::A, priority_in); + priority->setParam(ID::POLARITY, priority_pol); + + std::stringstream sstr; + sstr << "$procdff$" << (autoidx++); + + RTLIL::Cell *cell = mod->addDffsr(sstr.str(), sigs.clk, sig_sr_set, sig_sr_clr, sigs.d, sigs.q, clk_polarity); + cell->attributes = proc->attributes; + priority->attributes = proc->attributes; + + log(" created %s cell `%s' with %s edge clock and multiple level-sensitive resets.\n", + cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative"); +} + void gen_aldff(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_out, bool clk_polarity, bool set_polarity, RTLIL::SigSpec clk, RTLIL::SigSpec set, RTLIL::Process *proc) { @@ -269,7 +390,7 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) log_warning("Complex async reset for dff `%s'.\n", log_signal(sig)); DSigs sigs {insig, sig, sync_edge->signal}; bool clk_pol = sync_edge->type == RTLIL::SyncType::STp; - gen_dffsr_complex(mod, sigs, clk_pol, async_rules, proc); + gen_dffsr(mod, sigs, clk_pol, async_rules, ce, proc); continue; } @@ -313,6 +434,7 @@ struct ProcDffPass : public Pass { log_header(design, "Executing PROC_DFF pass (convert process syncs to FFs).\n"); extra_args(args, 1, design); + Pass::call(design, "dump"); for (auto mod : design->all_selected_modules()) { ConstEval ce(mod); From e139970a38e90df201e8e2bb03676e615bbb73bb Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 16 Jan 2026 16:42:42 +0100 Subject: [PATCH 05/19] proc_dff: fix enables for $dffsr --- passes/proc/proc_dff.cc | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 72f2dbf75..d07976a18 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -108,7 +108,8 @@ void gen_dffsr(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, bool trig_polarity; // true = active high, false = active low bool effect; // true = set, false = reset }; - std::vector> bit_rules(sigs.d.size()); + // nullopt rule = "this bit is not assigned to in this rule" + std::vector>> bit_rules(sigs.d.size()); // For checking consistent per-bit set/reset edges and bailing out on inconsistent std::optional bit_set_pol; std::optional bit_reset_pol; @@ -116,14 +117,14 @@ void gen_dffsr(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, for (auto it = async_rules.cbegin(); it != async_rules.cend(); it++) { const auto& [sync_value, rule] = *it; - log_debug("sync_value %s, rule:\n", log_signal(sync_value)); - for (int i = 0; i < sigs.d.size(); i++) { - log_debug("rule->signal %s\n", log_signal(rule->signal)); - log_debug(rule->signal.size() == 1); + log_assert(rule->signal.size() == 1); SigSpec value_bit = sync_value[i]; if (sync_value[i] == sigs.q[i]) { log_debug("%s is %s\n", log_signal(sync_value[i]), log_signal(sigs.q[i])); + while (bit_rules.size() <= (size_t) i) + bit_rules.push_back({}); + bit_rules[i].push_back(std::nullopt); continue; } if (!ce.eval(value_bit)) { @@ -135,7 +136,8 @@ void gen_dffsr(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, bool trig_pol = rule->type == RTLIL::SyncType::ST1; while (bit_rules.size() <= (size_t) i) bit_rules.push_back({}); - bit_rules[i].push_back({rule->signal[0], trig_pol, effect}); + BitRule bit_rule {rule->signal[0], trig_pol, effect}; + bit_rules[i].push_back(bit_rule); bool set_inconsistent = effect && bit_set_pol && (*bit_set_pol != trig_pol); bool reset_inconsistent = !effect && bit_reset_pol && (*bit_reset_pol != trig_pol); @@ -165,10 +167,17 @@ void gen_dffsr(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, SigSpec bit_sets; SigSpec bit_resets; for (auto rule : bit_rules[i]) { - log_debug("if %s == %d then set %d\n", log_signal(rule.trig), rule.trig_polarity, rule.effect); - priority_in.append(rule.trig); - priority_pol.push_back(RTLIL::State(rule.trig_polarity)); - if (rule.effect) + if (!rule) { + // Unused bit due to no assignment to this bit from this rule + log_debug("Unused bit due to no assignment to this bit from this rule\n"); + priority_in.append(Const(0, 1)); + priority_pol.push_back(RTLIL::State::S0); + continue; + } + log_debug("if %s == %d then set %d\n", log_signal(rule->trig), rule->trig_polarity, rule->effect); + priority_in.append(rule->trig); + priority_pol.push_back(RTLIL::State(rule->trig_polarity)); + if (rule->effect) bit_sets.append(SigBit(prioritized, priority_in.size() - 1)); else bit_resets.append(SigBit(prioritized, priority_in.size() - 1)); @@ -180,7 +189,7 @@ void gen_dffsr(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, } else { set = mod->addWire(NEW_ID); // Polarities are consistent, as guaranteed by check prior - (bit_rules[i][0].trig_polarity ? mod->addReduceOr(NEW_ID, bit_sets, *set) : mod->addReduceAnd(NEW_ID, bit_sets, *set))->attributes = proc->attributes; + (*bit_set_pol ? mod->addReduceOr(NEW_ID, bit_sets, *set) : mod->addReduceAnd(NEW_ID, bit_sets, *set))->attributes = proc->attributes; } } std::optional reset; @@ -189,7 +198,7 @@ void gen_dffsr(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, reset = bit_resets[0]; } else { reset = mod->addWire(NEW_ID); - (bit_rules[i][0].trig_polarity ? mod->addReduceOr(NEW_ID, bit_resets, *reset) : mod->addReduceAnd(NEW_ID, bit_resets, *reset))->attributes = proc->attributes; + (*bit_reset_pol ? mod->addReduceOr(NEW_ID, bit_resets, *reset) : mod->addReduceAnd(NEW_ID, bit_resets, *reset))->attributes = proc->attributes; } } if (set) From 6e280c0e0b96ba4e2ae73e4aeb02e587a49cb9c8 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 16 Jan 2026 23:30:41 +0100 Subject: [PATCH 06/19] proc_dff: uniquing $priority --- passes/proc/proc_dff.cc | 197 ++++++++++++++++++++++++++-------------- 1 file changed, 129 insertions(+), 68 deletions(-) diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index d07976a18..998d18bca 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -23,11 +23,25 @@ #include "kernel/sigtools.h" #include "kernel/consteval.h" #include "kernel/log.h" +#include "kernel/yosys_common.h" +#include #include #include #include USING_YOSYS_NAMESPACE + +struct BitRule { + SigBit trig; + bool trig_polarity; // true = active high, false = active low + bool effect; // true = set, false = reset + + bool operator==(const BitRule& other) const { return trig == other.trig && trig_polarity == other.trig_polarity && effect == other.effect; } + [[nodiscard]] Hasher hash_into(Hasher h) const { // No, this fluff doesn't deserve more lines. It's not meant to be read. + h.eat(trig); h.eat(trig_polarity); h.eat(effect); return h; } +}; +template<> struct std::hash> {std::size_t operator()(const std::vector& r) const noexcept { Hasher h; for (auto& rr : r) h.eat(rr); return (size_t)h.yield(); } }; + PRIVATE_NAMESPACE_BEGIN RTLIL::SigSpec find_any_lvalue(const RTLIL::Process *proc) @@ -62,6 +76,9 @@ struct DSigs { }; using Rules = std::vector>; +/** + * Generates odd $dffsr wirh priority and ALOAD implemented with muxes + */ void gen_dffsr_complex(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, Rules &async_rules, RTLIL::Process *proc) { @@ -97,22 +114,19 @@ void gen_dffsr_complex(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative"); } +/** + * Generates $dffsr wirh $priority cells + */ void gen_dffsr(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, Rules &async_rules, ConstEval& ce, RTLIL::Process *proc) { RTLIL::SigSpec sig_sr_set; RTLIL::SigSpec sig_sr_clr; - - struct BitRule { - SigBit trig; - bool trig_polarity; // true = active high, false = active low - bool effect; // true = set, false = reset - }; // nullopt rule = "this bit is not assigned to in this rule" std::vector>> bit_rules(sigs.d.size()); // For checking consistent per-bit set/reset edges and bailing out on inconsistent - std::optional bit_set_pol; - std::optional bit_reset_pol; + std::optional set_pol; + std::optional reset_pol; for (auto it = async_rules.cbegin(); it != async_rules.cend(); it++) { @@ -128,6 +142,7 @@ void gen_dffsr(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, continue; } if (!ce.eval(value_bit)) { + // ALOAD, mux tree time log_debug("non-const %s\n", log_signal(sync_value[i])); gen_dffsr_complex(mod, sigs, clk_polarity, async_rules, proc); return; @@ -139,86 +154,132 @@ void gen_dffsr(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, BitRule bit_rule {rule->signal[0], trig_pol, effect}; bit_rules[i].push_back(bit_rule); - bool set_inconsistent = effect && bit_set_pol && (*bit_set_pol != trig_pol); - bool reset_inconsistent = !effect && bit_reset_pol && (*bit_reset_pol != trig_pol); + bool set_inconsistent = effect && set_pol && (*set_pol != trig_pol); + bool reset_inconsistent = !effect && reset_pol && (*reset_pol != trig_pol); if (set_inconsistent || reset_inconsistent) { + // Mixed polarities, mux tree time gen_dffsr_complex(mod, sigs, clk_polarity, async_rules, proc); return; } if (effect) { - bit_set_pol = trig_pol; + set_pol = trig_pol; } else { - bit_reset_pol = trig_pol; + reset_pol = trig_pol; } } } - log_assert(bit_set_pol != std::nullopt); - log_assert(bit_reset_pol != std::nullopt); + log_assert(set_pol != std::nullopt); + log_assert(reset_pol != std::nullopt); - RTLIL::Wire* prioritized = mod->addWire(NEW_ID, sigs.d.size() * async_rules.size()); - RTLIL::Cell* priority = mod->addPriority(NEW_ID, SigSpec(), prioritized); - priority->setParam(ID::WIDTH, sigs.d.size()); - priority->setParam(ID::P_WIDTH, async_rules.size()); - SigSpec priority_in; - std::vector priority_pol; + struct Builder { + using BitControl = std::pair, std::optional>; + std::vector cells {}; + std::unordered_map, BitControl> map = {}; + RTLIL::Module* mod; + RTLIL::Wire* prioritized; + RTLIL::SigSpec priority_in; + std::vector priority_pol; + bool set_pol, reset_pol; + Builder(RTLIL::Module* mod, size_t rule_count, bool s, bool r) : mod(mod), set_pol(s), reset_pol(r) { + prioritized = mod->addWire(NEW_ID, 0); + RTLIL::Cell* priority = mod->addPriority(NEW_ID, SigSpec(), prioritized); + priority->setParam(ID::P_WIDTH, rule_count); + cells.push_back(priority); + } + BitControl build(std::vector>& rules) { + std::vector applicable; + int skips = 0; + for (auto rule : rules) { + if (rule) { + applicable.push_back(*rule); + } else { + skips += 1; + log_debug("Unused bit due to no assignment to this bit from this rule\n"); + } + } + log_debug("count?\n"); + if (map.count(applicable)) { + log_debug("hit!\n"); + return map[applicable]; + } + + SigSpec bit_sets; + SigSpec bit_resets; + + // Construct applicable rules + for (auto rule : applicable) { + log_debug("if %s == %d then set %d\n", log_signal(rule.trig), rule.trig_polarity, rule.effect); + prioritized->width++; + priority_in.append(rule.trig); + priority_pol.push_back(RTLIL::State(rule.trig_polarity)); + if (rule.effect) + bit_sets.append(SigBit(prioritized, priority_in.size() - 1)); + else + bit_resets.append(SigBit(prioritized, priority_in.size() - 1)); + } + + // Stuff $priority with unused bits + priority_in.append(Const(0, skips)); + for (int i = 0; i < skips; i++) { + prioritized->width++; + priority_pol.push_back(RTLIL::State::S0); + } + + std::optional set; + if (bit_sets.size()) { + if (bit_sets.size() == 1) { + set = bit_sets[0]; + } else { + set = mod->addWire(NEW_ID); + // Polarities are consistent, as guaranteed by check prior + cells.push_back(set_pol ? mod->addReduceOr(NEW_ID, bit_sets, *set) : mod->addReduceAnd(NEW_ID, bit_sets, *set)); + } + } + std::optional reset; + if (bit_resets.size()) { + if (bit_resets.size() == 1) { + reset = bit_resets[0]; + } else { + reset = mod->addWire(NEW_ID); + cells.push_back(reset_pol ? mod->addReduceOr(NEW_ID, bit_resets, *reset) : mod->addReduceAnd(NEW_ID, bit_resets, *reset)); + } + } + if (!set) + set = set_pol ? RTLIL::State::S0 : RTLIL::State::S1; + if (!reset) + reset = reset_pol ? RTLIL::State::S0 : RTLIL::State::S1; + + auto ret = std::make_pair(set, reset); + map[applicable] = ret; + return ret; + + } + void finish(RTLIL::Process* proc) { + prioritized->attributes = proc->attributes; + for (auto* cell : cells) + cell->attributes = proc->attributes; + + cells[0]->setPort(ID::A, priority_in); + cells[0]->setPort(ID::Y, prioritized); // fixup (previously zero-width) + cells[0]->setParam(ID::POLARITY, priority_pol); + cells[0]->setParam(ID::WIDTH, cells[0]->getPort(ID::A).size() / cells[0]->getParam(ID::P_WIDTH).as_int()); + } + }; + Builder builder(mod, async_rules.size(), *set_pol, *reset_pol); for (int i = 0; i < sigs.d.size(); i++) { log_debug("bit %d:\n", i); - SigSpec bit_sets; - SigSpec bit_resets; - for (auto rule : bit_rules[i]) { - if (!rule) { - // Unused bit due to no assignment to this bit from this rule - log_debug("Unused bit due to no assignment to this bit from this rule\n"); - priority_in.append(Const(0, 1)); - priority_pol.push_back(RTLIL::State::S0); - continue; - } - log_debug("if %s == %d then set %d\n", log_signal(rule->trig), rule->trig_polarity, rule->effect); - priority_in.append(rule->trig); - priority_pol.push_back(RTLIL::State(rule->trig_polarity)); - if (rule->effect) - bit_sets.append(SigBit(prioritized, priority_in.size() - 1)); - else - bit_resets.append(SigBit(prioritized, priority_in.size() - 1)); - } - std::optional set; - if (bit_sets.size()) { - if (bit_sets.size() == 1) { - set = bit_sets[0]; - } else { - set = mod->addWire(NEW_ID); - // Polarities are consistent, as guaranteed by check prior - (*bit_set_pol ? mod->addReduceOr(NEW_ID, bit_sets, *set) : mod->addReduceAnd(NEW_ID, bit_sets, *set))->attributes = proc->attributes; - } - } - std::optional reset; - if (bit_resets.size()) { - if (bit_resets.size() == 1) { - reset = bit_resets[0]; - } else { - reset = mod->addWire(NEW_ID); - (*bit_reset_pol ? mod->addReduceOr(NEW_ID, bit_resets, *reset) : mod->addReduceAnd(NEW_ID, bit_resets, *reset))->attributes = proc->attributes; - } - } - if (set) - sig_sr_set.append(*set); - else - sig_sr_set.append(*bit_set_pol ? Const(0, 1) : Const(1, 1)); - if (reset) - sig_sr_clr.append(*reset); - else - sig_sr_clr.append(*bit_reset_pol ? Const(0, 1) : Const(1, 1)); + auto [set, reset] = builder.build(bit_rules[i]); + sig_sr_set.append(*set); + sig_sr_clr.append(*reset); } - priority->setPort(ID::A, priority_in); - priority->setParam(ID::POLARITY, priority_pol); + builder.finish(proc); std::stringstream sstr; sstr << "$procdff$" << (autoidx++); RTLIL::Cell *cell = mod->addDffsr(sstr.str(), sigs.clk, sig_sr_set, sig_sr_clr, sigs.d, sigs.q, clk_polarity); cell->attributes = proc->attributes; - priority->attributes = proc->attributes; log(" created %s cell `%s' with %s edge clock and multiple level-sensitive resets.\n", cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative"); From d027b62d1cef3392442b207ccfe508c9379a1b97 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 16 Jan 2026 23:46:38 +0100 Subject: [PATCH 07/19] proc_dff: fix missing polarity parameters for $dffsr, add another fallback --- passes/proc/proc_dff.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 998d18bca..0bd442e2e 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -169,8 +169,12 @@ void gen_dffsr(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, } } - log_assert(set_pol != std::nullopt); - log_assert(reset_pol != std::nullopt); + if (set_pol == std::nullopt || reset_pol == std::nullopt) { + // set or reset never used, falling back to mux tree + gen_dffsr_complex(mod, sigs, clk_polarity, async_rules, proc); + return; + } + struct Builder { using BitControl = std::pair, std::optional>; @@ -280,6 +284,8 @@ void gen_dffsr(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, RTLIL::Cell *cell = mod->addDffsr(sstr.str(), sigs.clk, sig_sr_set, sig_sr_clr, sigs.d, sigs.q, clk_polarity); cell->attributes = proc->attributes; + cell->setParam(ID::SET_POLARITY, Const(*set_pol, 1)); + cell->setParam(ID::CLR_POLARITY, Const(*reset_pol, 1)); log(" created %s cell `%s' with %s edge clock and multiple level-sensitive resets.\n", cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative"); From 8192cee0b3e435482f2bc98a26899db17efec7e9 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 19 Jan 2026 13:40:27 +0100 Subject: [PATCH 08/19] proc_dff: narrow $priority --- passes/proc/proc_dff.cc | 123 ++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 0bd442e2e..5004bf4cd 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -177,44 +177,46 @@ void gen_dffsr(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, struct Builder { - using BitControl = std::pair, std::optional>; - std::vector cells {}; - std::unordered_map, BitControl> map = {}; + using MaybeBitControl = std::pair, std::optional>; + std::unordered_map, MaybeBitControl> map = {}; RTLIL::Module* mod; - RTLIL::Wire* prioritized; - RTLIL::SigSpec priority_in; - std::vector priority_pol; + RTLIL::Process* proc; bool set_pol, reset_pol; - Builder(RTLIL::Module* mod, size_t rule_count, bool s, bool r) : mod(mod), set_pol(s), reset_pol(r) { - prioritized = mod->addWire(NEW_ID, 0); - RTLIL::Cell* priority = mod->addPriority(NEW_ID, SigSpec(), prioritized); - priority->setParam(ID::P_WIDTH, rule_count); - cells.push_back(priority); + RTLIL::SigBit const_if_none(std::optional maybe, bool polarity) { + if (maybe) + return *maybe; + // Set the control signal to be constantly inactive + if (polarity) + return set_pol ? RTLIL::State::S0 : RTLIL::State::S1; + else + return reset_pol ? RTLIL::State::S0 : RTLIL::State::S1; } - BitControl build(std::vector>& rules) { - std::vector applicable; - int skips = 0; - for (auto rule : rules) { - if (rule) { - applicable.push_back(*rule); - } else { - skips += 1; - log_debug("Unused bit due to no assignment to this bit from this rule\n"); - } - } - log_debug("count?\n"); - if (map.count(applicable)) { - log_debug("hit!\n"); - return map[applicable]; + Builder(RTLIL::Module* mod, RTLIL::Process* proc, bool s, bool r) : mod(mod), proc(proc), set_pol(s), reset_pol(r) {} + std::pair priority(const std::vector& applicable) { + RTLIL::SigSpec bit_sets {}; + RTLIL::SigSpec bit_resets {}; + if (!applicable.size()) { + return std::make_pair(bit_sets, bit_resets); + } else if (applicable.size() == 1) { + auto rule = applicable[0]; + if (rule.effect) + bit_sets.append(rule.trig); + else + bit_resets.append(rule.trig); + return std::make_pair(bit_sets, bit_resets); } + RTLIL::Wire* prioritized = mod->addWire(NEW_ID, applicable.size()); + prioritized->attributes = proc->attributes; + RTLIL::Cell* priority = mod->addPriority(NEW_ID, SigSpec(), prioritized); + priority->attributes = proc->attributes; + priority->setParam(ID::WIDTH, applicable.size()); - SigSpec bit_sets; - SigSpec bit_resets; + RTLIL::SigSpec priority_in; + std::vector priority_pol; // Construct applicable rules for (auto rule : applicable) { log_debug("if %s == %d then set %d\n", log_signal(rule.trig), rule.trig_polarity, rule.effect); - prioritized->width++; priority_in.append(rule.trig); priority_pol.push_back(RTLIL::State(rule.trig_polarity)); if (rule.effect) @@ -223,70 +225,67 @@ void gen_dffsr(RTLIL::Module *mod, DSigs sigs, bool clk_polarity, bit_resets.append(SigBit(prioritized, priority_in.size() - 1)); } - // Stuff $priority with unused bits - priority_in.append(Const(0, skips)); - for (int i = 0; i < skips; i++) { - prioritized->width++; - priority_pol.push_back(RTLIL::State::S0); - } - - std::optional set; + priority->setPort(ID::A, priority_in); + priority->setPort(ID::Y, prioritized); // fixup (previously zero-width) + priority->setParam(ID::POLARITY, priority_pol); + return std::make_pair(bit_sets, bit_resets); + } + std::pair reduce(const std::pair& unreduced) { + const auto& [bit_sets, bit_resets] = unreduced; + std::optional set, reset; if (bit_sets.size()) { if (bit_sets.size() == 1) { set = bit_sets[0]; } else { set = mod->addWire(NEW_ID); - // Polarities are consistent, as guaranteed by check prior - cells.push_back(set_pol ? mod->addReduceOr(NEW_ID, bit_sets, *set) : mod->addReduceAnd(NEW_ID, bit_sets, *set)); + set->wire->attributes = proc->attributes; + // Polarities are required to be consistent, as guaranteed by check above buildere + (set_pol ? mod->addReduceOr(NEW_ID, bit_sets, *set) : mod->addReduceAnd(NEW_ID, bit_sets, *set))->attributes = proc->attributes; } } - std::optional reset; if (bit_resets.size()) { if (bit_resets.size() == 1) { reset = bit_resets[0]; } else { reset = mod->addWire(NEW_ID); - cells.push_back(reset_pol ? mod->addReduceOr(NEW_ID, bit_resets, *reset) : mod->addReduceAnd(NEW_ID, bit_resets, *reset)); + reset->wire->attributes = proc->attributes; + (reset_pol ? mod->addReduceOr(NEW_ID, bit_resets, *reset) : mod->addReduceAnd(NEW_ID, bit_resets, *reset))->attributes = proc->attributes; } } - if (!set) - set = set_pol ? RTLIL::State::S0 : RTLIL::State::S1; - if (!reset) - reset = reset_pol ? RTLIL::State::S0 : RTLIL::State::S1; + return std::make_pair(const_if_none(set, true), const_if_none(reset, false)); + } + MaybeBitControl build(std::vector>& rules) { + std::vector applicable; + for (auto rule : rules) { + if (rule) { + applicable.push_back(*rule); + } else { + log_debug("Unused bit due to no assignment to this bit from this rule\n"); + } + } + if (map.count(applicable)) { + return map[applicable]; + } - auto ret = std::make_pair(set, reset); + auto priority_controls = priority(applicable); + auto ret = reduce(priority_controls); map[applicable] = ret; return ret; - - } - void finish(RTLIL::Process* proc) { - prioritized->attributes = proc->attributes; - for (auto* cell : cells) - cell->attributes = proc->attributes; - - cells[0]->setPort(ID::A, priority_in); - cells[0]->setPort(ID::Y, prioritized); // fixup (previously zero-width) - cells[0]->setParam(ID::POLARITY, priority_pol); - cells[0]->setParam(ID::WIDTH, cells[0]->getPort(ID::A).size() / cells[0]->getParam(ID::P_WIDTH).as_int()); } }; - Builder builder(mod, async_rules.size(), *set_pol, *reset_pol); + Builder builder(mod, proc, *set_pol, *reset_pol); for (int i = 0; i < sigs.d.size(); i++) { - log_debug("bit %d:\n", i); auto [set, reset] = builder.build(bit_rules[i]); sig_sr_set.append(*set); sig_sr_clr.append(*reset); } - builder.finish(proc); std::stringstream sstr; sstr << "$procdff$" << (autoidx++); - RTLIL::Cell *cell = mod->addDffsr(sstr.str(), sigs.clk, sig_sr_set, sig_sr_clr, sigs.d, sigs.q, clk_polarity); cell->attributes = proc->attributes; cell->setParam(ID::SET_POLARITY, Const(*set_pol, 1)); cell->setParam(ID::CLR_POLARITY, Const(*reset_pol, 1)); - log(" created %s cell `%s' with %s edge clock and multiple level-sensitive resets.\n", cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative"); } From a7286ca8f5c21a0fe4fb7da6da2bb67d89e2421a Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 19 Jan 2026 18:57:52 +0100 Subject: [PATCH 09/19] simlib: fix $priority --- techlibs/common/simlib.v | 20 ++++++++++++++++++-- techlibs/common/techmap.v | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index fcdbab555..59209a422 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -3259,10 +3259,26 @@ endmodule //- Priority operator. An output bit is set if the input bit at the same index is set and no lower index input bit is set. //- module \$priority (A, Y); -parameter WIDTH = 8; +parameter WIDTH = 0; +parameter POLARITY = 0; input [WIDTH-1:0] A; output [WIDTH-1:0] Y; -assign Y = A & (~A + 1); +wire [WIDTH-1:0] tmp; +wire [WIDTH-1:0] A_active; +wire [WIDTH-1:0] Y_active; +assign A_active = A ^ ~POLARITY; +assign Y = Y_active ^ ~POLARITY; +genvar i; +generate + if (WIDTH > 0) begin + assign tmp[0] = A_active[0]; + assign Y_active[0] = A_active[0]; + end + for (i = 1; i < WIDTH; i = i + 1) begin + assign Y_active[i] = tmp[i-1] ? 1'b0 : A_active[i]; + assign tmp[i] = tmp[i-1] | A_active[i]; + end +endgenerate endmodule diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v index c8f4ec420..c13f1231d 100644 --- a/techlibs/common/techmap.v +++ b/techlibs/common/techmap.v @@ -694,6 +694,7 @@ module \$priority (A, Y); wire [WIDTH-1:0] tmp; (* force_downto *) wire [WIDTH-1:0] A_active; + (* force_downto *) wire [WIDTH-1:0] Y_active; assign A_active = A ^ ~POLARITY; assign Y = Y_active ^ ~POLARITY; From f528e1cea1f077051ed7e49fc3eeefff90979694 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 19 Jan 2026 18:58:08 +0100 Subject: [PATCH 10/19] simplemap: add $priority --- passes/techmap/simplemap.cc | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index 0c7d1930e..a0b478d1d 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -18,6 +18,7 @@ */ #include "simplemap.h" +#include "kernel/rtlil.h" #include "kernel/sigtools.h" #include "kernel/ff.h" #include @@ -27,11 +28,11 @@ USING_YOSYS_NAMESPACE YOSYS_NAMESPACE_BEGIN -static void transfer_attr (Cell* to, const Cell* from, IdString attr) { +static void transfer_attr (RTLIL::AttrObject* to, const RTLIL::AttrObject* from, IdString attr) { if (from->has_attribute(attr)) to->attributes[attr] = from->attributes.at(attr); } -static void transfer_src (Cell* to, const Cell* from) { +static void transfer_src (RTLIL::AttrObject* to, const RTLIL::AttrObject* from) { transfer_attr(to, from, ID::src); } @@ -438,6 +439,37 @@ void simplemap_ff(RTLIL::Module *, RTLIL::Cell *cell) } } +void simplemap_priority(RTLIL::Module *module, RTLIL::Cell *cell) +{ + int width = cell->getParam(ID::WIDTH).as_int(); + RTLIL::Const polarity = cell->getParam(ID::POLARITY); + RTLIL::Wire* any_previous_active = module->addWire(NEW_ID, width); + transfer_src(any_previous_active, cell); + RTLIL::Wire* active = module->addWire(NEW_ID, width); + transfer_src(active, cell); + RTLIL::SigSpec a = cell->getPort(ID::A); + RTLIL::SigSpec y = cell->getPort(ID::Y); + if (width) { + RTLIL::State active_val = polarity[0] ? RTLIL::State::S1 : RTLIL::State::S0; + RTLIL::Cell* xnor = module->addXnorGate(NEW_ID, a[0], active_val, {RTLIL::SigBit(any_previous_active, 0)}); + transfer_src(xnor, cell); + module->connect({y[0], a[0]}); + } + for (int i = 1; i < width; i++) { + RTLIL::State inactive_val = !polarity[i] ? RTLIL::State::S1 : RTLIL::State::S0; + RTLIL::State active_val = polarity[i] ? RTLIL::State::S1 : RTLIL::State::S0; + RTLIL::SigBit this_active = {active, i}; + RTLIL::SigBit active_so_far = {any_previous_active, i - 1}; + RTLIL::SigBit next_active_so_far = {any_previous_active, i}; + RTLIL::Cell* mux = module->addMuxGate(NEW_ID, a[i], inactive_val, active_so_far, y[i]); + RTLIL::Cell* xnor = module->addXnorGate(NEW_ID, a[i], active_val, this_active); + RTLIL::Cell* or_ = module->addOrGate(NEW_ID, active_so_far, this_active, next_active_so_far); + transfer_src(xnor, cell); + transfer_src(mux, cell); + transfer_src(or_, cell); + } +} + void simplemap_get_mappers(dict &mappers) { mappers[ID($not)] = simplemap_not; @@ -484,6 +516,7 @@ void simplemap_get_mappers(dict mappers[ID($dlatch)] = simplemap_ff; mappers[ID($adlatch)] = simplemap_ff; mappers[ID($dlatchsr)] = simplemap_ff; + mappers[ID($priority)] = simplemap_priority; } void simplemap(RTLIL::Module *module, RTLIL::Cell *cell) From f785b664af13765793191b8fbf1b5ba041ab64d0 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 19 Jan 2026 19:18:45 +0100 Subject: [PATCH 11/19] verilog_backend: simplemap $priority, ruining everything --- backends/verilog/verilog_backend.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 3d451117c..04d68519f 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -2680,6 +2680,7 @@ struct VerilogBackend : public Backend { if (!noexpr) { Pass::call(design, "bmuxmap"); Pass::call(design, "demuxmap"); + Pass::call(design, "simplemap t:$priority"); } Pass::call(design, "clean_zerowidth"); log_pop(); From dcd73b6b03347d788bef32abddef22fd1c07be13 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 19 Jan 2026 19:51:09 +0100 Subject: [PATCH 12/19] verilog_backend: alter only design copy unless -wreck --- backends/verilog/verilog_backend.cc | 32 +++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 04d68519f..7a0ea3b85 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -95,7 +95,7 @@ bool VERILOG_BACKEND::id_is_verilog_escaped(const std::string &str) { PRIVATE_NAMESPACE_BEGIN -bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog, simple_lhs, noparallelcase; +bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog, simple_lhs, noparallelcase, wreck; int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter; dict auto_name_map; std::set reg_wires; @@ -2554,6 +2554,10 @@ struct VerilogBackend : public Backend { log(" only write selected modules. modules must be selected entirely or\n"); log(" not at all.\n"); log("\n"); + log(" -wreck\n"); + log(" wreck your design by running required preparation passes\n"); + log(" on it, instead of a copy. Used to be the default.\n"); + log("\n"); log(" -v\n"); log(" verbose output (print new names of all renamed wires and cells)\n"); log("\n"); @@ -2666,6 +2670,10 @@ struct VerilogBackend : public Backend { verbose = true; continue; } + if (arg == "-wreck") { + wreck = true; + continue; + } break; } extra_args(f, filename, args, argidx); @@ -2676,6 +2684,21 @@ struct VerilogBackend : public Backend { extmem_prefix = filename.substr(0, filename.rfind('.')); } + RTLIL::Design *original_design; + if (!wreck) { + // FIXME shamefully copied out of design.cc + RTLIL::Design * design_copy = new RTLIL::Design; + + for (auto mod : design->modules()) + design_copy->add(mod->clone()); + + design_copy->selection_stack = design->selection_stack; + design_copy->selection_vars = design->selection_vars; + design_copy->selected_active_module = design->selected_active_module; + original_design = design; + design = design_copy; + } + log_push(); if (!noexpr) { Pass::call(design, "bmuxmap"); @@ -2685,7 +2708,7 @@ struct VerilogBackend : public Backend { Pass::call(design, "clean_zerowidth"); log_pop(); - design->sort_modules(); + design->sort_modules(); *f << stringf("/* Generated by %s */\n", yosys_maybe_version()); @@ -2702,6 +2725,11 @@ struct VerilogBackend : public Backend { dump_module(*f, "", module); } + if (!wreck) { + RTLIL::Design * design_copy = design; + design = original_design; + delete design_copy; + } auto_name_map.clear(); reg_wires.clear(); } From 3ea54372985f5b88ac08300dbf6ba455fbeb46c8 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 20 Jan 2026 12:59:35 +0100 Subject: [PATCH 13/19] satgen: $priority can't have x on any output bit above an active input bit --- kernel/satgen.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/satgen.cc b/kernel/satgen.cc index c608a0c19..898d645f7 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -438,16 +438,16 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) const Const& polarity = cell->getParam(ID::POLARITY); - int any_previous_active; + std::vector active_so_far; if (a.size()) { - any_previous_active = polarity[0] ? a[0] : ez->NOT(a[0]); + active_so_far.push_back(polarity[0] ? a[0] : ez->NOT(a[0])); ez->assume(ez->IFF(yy[0], a[0])); } for (size_t i = 1; i < a.size(); i++) { int inactive_val = !polarity[i] ? ez->CONST_TRUE : ez->CONST_FALSE; int active_val = polarity[i] ? ez->CONST_TRUE : ez->CONST_FALSE; - ez->assume(ez->IFF(yy[i], ez->ITE(any_previous_active, inactive_val, a[i]))); - any_previous_active = ez->OR(any_previous_active, ez->IFF(a[i], active_val)); + ez->assume(ez->IFF(yy[i], ez->ITE(active_so_far.back(), inactive_val, a[i]))); + active_so_far.push_back(ez->OR(active_so_far.back(), ez->IFF(a[i], active_val))); } if (model_undef) { std::vector undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep); @@ -460,7 +460,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) } for (size_t i = 1; i < a.size(); i++) { any_previous_undef = ez->OR(any_previous_undef, undef_a[i]); - ez->assume(ez->IFF(undef_y[i], any_previous_undef)); + ez->assume(ez->IFF(undef_y[i], ez->AND(ez->NOT(active_so_far[i - 1]), any_previous_undef))); } undefGating(y, yy, undef_y); } From ed58bb6bb49ee1d3f309a32afbd06f96fba6e0eb Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 20 Jan 2026 13:00:12 +0100 Subject: [PATCH 14/19] clk2fflogic: $dffsr has undef output on S&R --- passes/sat/clk2fflogic.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index dd94dd0d7..b75c8aab1 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -123,10 +123,14 @@ struct Clk2fflogicPass : public Pass { return module->Mux(NEW_ID, a, b, s); } SigSpec bitwise_sr(Module *module, SigSpec a, SigSpec s, SigSpec r, bool is_fine) { - if (is_fine) - return module->AndGate(NEW_ID, module->OrGate(NEW_ID, a, s), module->NotGate(NEW_ID, r)); - else - return module->And(NEW_ID, module->Or(NEW_ID, a, s), module->Not(NEW_ID, r)); + if (is_fine) { + return module->MuxGate(NEW_ID, module->AndGate(NEW_ID, module->OrGate(NEW_ID, a, s), module->NotGate(NEW_ID, r)), RTLIL::State::Sx, module->AndGate(NEW_ID, s, r)); + } else { + std::vector y; + for (int i = 0; i < a.size(); i++) + y.push_back(module->MuxGate(NEW_ID, module->AndGate(NEW_ID, module->OrGate(NEW_ID, a[i], s[i]), module->NotGate(NEW_ID, r[i])), RTLIL::State::Sx, module->AndGate(NEW_ID, s[i], r[i]))); + return y; + } } void execute(std::vector args, RTLIL::Design *design) override { From 39d960344bbd8cea21f462e99b14ba1d18221469 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 20 Jan 2026 19:44:10 +0100 Subject: [PATCH 15/19] satgen: $priority match x-prop against simplemap --- kernel/satgen.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kernel/satgen.cc b/kernel/satgen.cc index 898d645f7..c6b4829ad 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -459,8 +459,14 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) ez->assume(ez->IFF(undef_y[0], undef_a[0])); } for (size_t i = 1; i < a.size(); i++) { - any_previous_undef = ez->OR(any_previous_undef, undef_a[i]); - ez->assume(ez->IFF(undef_y[i], ez->AND(ez->NOT(active_so_far[i - 1]), any_previous_undef))); + int inactive_val = !polarity[i] ? ez->CONST_TRUE : ez->CONST_FALSE; + // $mux emulation + int is_active = ez->XOR(inactive_val, a[i]); + int undef_because_s = ez->AND(any_previous_undef, ez->OR(is_active, undef_a[i])); + int undef_because_a = ez->AND(ez->OR(any_previous_undef, ez->NOT(active_so_far[i-1])), undef_a[i]); + int undef = ez->OR(undef_because_s, undef_because_a); + any_previous_undef = ez->OR(any_previous_undef, is_active); + ez->assume(ez->IFF(undef_y[i], undef)); } undefGating(y, yy, undef_y); } From 6a5d74d252d0645b581089fca91d1aa3d0126b39 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 20 Jan 2026 23:07:33 +0100 Subject: [PATCH 16/19] satgen: $priority perform more x-bit horrors --- kernel/satgen.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/satgen.cc b/kernel/satgen.cc index c6b4829ad..bfbf6d77e 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -465,7 +465,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) int undef_because_s = ez->AND(any_previous_undef, ez->OR(is_active, undef_a[i])); int undef_because_a = ez->AND(ez->OR(any_previous_undef, ez->NOT(active_so_far[i-1])), undef_a[i]); int undef = ez->OR(undef_because_s, undef_because_a); - any_previous_undef = ez->OR(any_previous_undef, is_active); + any_previous_undef = ez->AND(ez->NOT(is_active), ez->OR(any_previous_undef, undef_a[i])); ez->assume(ez->IFF(undef_y[i], undef)); } undefGating(y, yy, undef_y); From 1d468962b7be1c494e999ae5ec00077f00c60adb Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 21 Jan 2026 11:50:45 +0100 Subject: [PATCH 17/19] satgen, calc: $priority perform more x-bit horrors --- kernel/calc.cc | 27 ++++++++++++++------------- kernel/satgen.cc | 13 ++++++++++--- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/kernel/calc.cc b/kernel/calc.cc index a1ba5f7c8..feaca4cc6 100644 --- a/kernel/calc.cc +++ b/kernel/calc.cc @@ -660,21 +660,22 @@ RTLIL::Const RTLIL::const_bmux(const RTLIL::Const &arg1, const RTLIL::Const &arg RTLIL::Const RTLIL::const_priority(const RTLIL::Const &arg, const RTLIL::Const &polarity) { - std::vector t; - std::optional first_non_zero = std::nullopt; - for (int i = 0; i < GetSize(arg); i++) + std::vector t; + RTLIL::State previous; + if (GetSize(arg)) { + RTLIL::State s = arg.at(0); + t.push_back(s); + previous = polarity[0] ? s : const_not(s, Const(), false, false, 1)[0]; + } + for (int i = 1; i < GetSize(arg); i++) { RTLIL::State s = arg.at(i); - if (first_non_zero && s != State::Sx) { - auto inactive = polarity[i] == State::S0 ? State::S1 : State::S0; - auto val = *first_non_zero == State::Sx ? State::Sx : inactive; - t.push_back(val); - } else { - t.push_back(s); - } - if ((!first_non_zero && s == polarity[i]) || s == State::Sx) { - first_non_zero = s; - } + RTLIL::State is_active = const_xnor(s, polarity[i], false, false, 1)[0]; + RTLIL::State next = const_or(is_active, previous, false, false, 1)[0]; + RTLIL::State inactive_polarity = const_not(polarity[i], Const(), false, false, 1)[0]; + RTLIL::State y = const_mux(s, inactive_polarity, previous)[0]; + t.push_back(y); + previous = next; } return t; } diff --git a/kernel/satgen.cc b/kernel/satgen.cc index bfbf6d77e..ce0c62819 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -460,13 +460,20 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) } for (size_t i = 1; i < a.size(); i++) { int inactive_val = !polarity[i] ? ez->CONST_TRUE : ez->CONST_FALSE; - // $mux emulation int is_active = ez->XOR(inactive_val, a[i]); - int undef_because_s = ez->AND(any_previous_undef, ez->OR(is_active, undef_a[i])); + int is_not_inactive = ez->OR(is_active, undef_a[i]); + int is_not_active = ez->OR(ez->NOT(is_active), undef_a[i]); + + // $mux + int undef_because_s = ez->AND(any_previous_undef, is_not_inactive); int undef_because_a = ez->AND(ez->OR(any_previous_undef, ez->NOT(active_so_far[i-1])), undef_a[i]); int undef = ez->OR(undef_because_s, undef_because_a); - any_previous_undef = ez->AND(ez->NOT(is_active), ez->OR(any_previous_undef, undef_a[i])); ez->assume(ez->IFF(undef_y[i], undef)); + + // $or + int next_previous_undef_because_previous = ez->AND(any_previous_undef, is_not_active); + int next_previous_undef_because_a = ez->AND(ez->NOT(active_so_far[i-1]), undef_a[i]); + any_previous_undef = ez->OR(next_previous_undef_because_previous, next_previous_undef_because_a); } undefGating(y, yy, undef_y); } From 3a5c492e32dc6e05f25831a1e5f2491e64e2dd3b Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 21 Jan 2026 12:33:22 +0100 Subject: [PATCH 18/19] clk2fflogic: fully cover $dffsr tests --- tests/sat/clk2fflogic.ys | 65 +++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/tests/sat/clk2fflogic.ys b/tests/sat/clk2fflogic.ys index 6d6d9e490..3da3d4e50 100644 --- a/tests/sat/clk2fflogic.ys +++ b/tests/sat/clk2fflogic.ys @@ -1,5 +1,5 @@ -read_verilog -icells < Date: Wed, 21 Jan 2026 20:55:32 +0100 Subject: [PATCH 19/19] cellaigs: add $priority --- kernel/cellaigs.cc | 21 +++++++++++ tests/aiger/gold/_priority_00000.aag | 5 +++ tests/aiger/gold/_reduce_and_00000.aag | 9 +++-- tests/aiger/gold/_reduce_bool_00000.aag | 12 +++---- tests/aiger/gold/_reduce_or_00000.aag | 11 +++--- tests/aiger/gold/_reduce_xnor_00000.aag | 30 +++------------- tests/aiger/gold/_reduce_xor_00000.aag | 10 +++--- tests/aiger/gold/_sub_00000.aag | 39 +++++++++++++------- tests/aiger/gold/_xnor_00000.aag | 10 ++++-- tests/aiger/gold/_xor_00000.aag | 47 ++++++++++++++++++++++--- 10 files changed, 130 insertions(+), 64 deletions(-) create mode 100644 tests/aiger/gold/_priority_00000.aag diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc index 0f897cd58..00be50bab 100644 --- a/kernel/cellaigs.cc +++ b/kernel/cellaigs.cc @@ -501,6 +501,27 @@ Aig::Aig(Cell *cell) goto optimize; } + if (cell->type == ID($priority)) + { + int width = GetSize(cell->getPort(ID::Y)); + RTLIL::Const polarity = cell->getParam(ID::POLARITY); + vector A = mk.inport_vec(ID::A, width); + vector Y; + int any_previous_active; + if (width) { + any_previous_active = polarity[0] ? A[0] : mk.not_gate(A[0]); + Y.push_back(A[0]); + } + for (int i = 1; i < width; i++) { + int inactive_val = mk.bool_node(!polarity[i]); + Y.push_back(mk.mux_gate(A[i], inactive_val, any_previous_active)); + int is_active = mk.xnor_gate(inactive_val, A[i]); + any_previous_active = mk.or_gate(any_previous_active, is_active); + } + mk.outport_vec(Y, ID::Y); + goto optimize; + } + name.clear(); return; diff --git a/tests/aiger/gold/_priority_00000.aag b/tests/aiger/gold/_priority_00000.aag new file mode 100644 index 000000000..7a4cd3156 --- /dev/null +++ b/tests/aiger/gold/_priority_00000.aag @@ -0,0 +1,5 @@ +aag 1 1 0 1 0 +2 +2 +c +Generated by Yosys diff --git a/tests/aiger/gold/_reduce_and_00000.aag b/tests/aiger/gold/_reduce_and_00000.aag index 06db9a1dd..7528ef437 100644 --- a/tests/aiger/gold/_reduce_and_00000.aag +++ b/tests/aiger/gold/_reduce_and_00000.aag @@ -1,7 +1,12 @@ -aag 1 1 0 3 0 -2 +aag 5 3 0 4 2 2 +4 +6 +10 0 0 +0 +8 4 2 +10 8 6 c Generated by Yosys diff --git a/tests/aiger/gold/_reduce_bool_00000.aag b/tests/aiger/gold/_reduce_bool_00000.aag index 48a1410c8..a33077f69 100644 --- a/tests/aiger/gold/_reduce_bool_00000.aag +++ b/tests/aiger/gold/_reduce_bool_00000.aag @@ -1,13 +1,9 @@ -aag 7 4 0 3 3 +aag 5 3 0 1 2 2 4 6 -8 -15 -0 -0 -10 5 3 -12 10 7 -14 12 9 +11 +8 5 3 +10 8 7 c Generated by Yosys diff --git a/tests/aiger/gold/_reduce_or_00000.aag b/tests/aiger/gold/_reduce_or_00000.aag index d5e8085c2..5fb08a56c 100644 --- a/tests/aiger/gold/_reduce_or_00000.aag +++ b/tests/aiger/gold/_reduce_or_00000.aag @@ -1,7 +1,10 @@ -aag 3 2 0 1 1 +aag 1 1 0 6 0 2 -4 -7 -6 5 3 +2 +0 +0 +0 +0 +0 c Generated by Yosys diff --git a/tests/aiger/gold/_reduce_xnor_00000.aag b/tests/aiger/gold/_reduce_xnor_00000.aag index 6ad6ac39e..05bc1659c 100644 --- a/tests/aiger/gold/_reduce_xnor_00000.aag +++ b/tests/aiger/gold/_reduce_xnor_00000.aag @@ -1,30 +1,10 @@ -aag 25 6 0 2 19 +aag 5 2 0 2 3 2 4 -6 -8 -10 -12 -51 +11 0 -14 4 2 -16 5 3 -18 17 15 -20 18 6 -22 17 15 -24 23 7 -26 25 21 -28 26 8 -30 25 21 -32 31 9 -34 33 29 -36 34 10 -38 33 29 -40 39 11 -42 41 37 -44 42 12 -46 41 37 -48 47 13 -50 49 45 +6 4 2 +8 5 3 +10 9 7 c Generated by Yosys diff --git a/tests/aiger/gold/_reduce_xor_00000.aag b/tests/aiger/gold/_reduce_xor_00000.aag index ab4bc32ad..06db9a1dd 100644 --- a/tests/aiger/gold/_reduce_xor_00000.aag +++ b/tests/aiger/gold/_reduce_xor_00000.aag @@ -1,9 +1,7 @@ -aag 5 2 0 1 3 +aag 1 1 0 3 0 2 -4 -10 -6 4 2 -8 5 3 -10 9 7 +2 +0 +0 c Generated by Yosys diff --git a/tests/aiger/gold/_sub_00000.aag b/tests/aiger/gold/_sub_00000.aag index e9d976b32..f81a90b30 100644 --- a/tests/aiger/gold/_sub_00000.aag +++ b/tests/aiger/gold/_sub_00000.aag @@ -1,20 +1,35 @@ -aag 16 13 0 1 3 +aag 29 6 0 3 23 2 4 6 8 10 12 -14 -16 -18 -20 -22 -24 -26 -33 -28 17 2 -30 16 3 -32 31 29 +19 +38 +58 +14 7 2 +16 6 3 +18 17 15 +20 6 3 +22 20 15 +24 9 4 +26 8 5 +28 27 25 +30 28 23 +32 27 25 +34 20 15 +36 34 33 +38 37 31 +40 27 23 +42 41 25 +44 11 4 +46 10 5 +48 47 45 +50 48 43 +52 47 45 +54 41 25 +56 54 53 +58 57 51 c Generated by Yosys diff --git a/tests/aiger/gold/_xnor_00000.aag b/tests/aiger/gold/_xnor_00000.aag index 9dd097862..85cf802b0 100644 --- a/tests/aiger/gold/_xnor_00000.aag +++ b/tests/aiger/gold/_xnor_00000.aag @@ -1,12 +1,16 @@ -aag 10 4 0 4 6 +aag 10 4 0 8 6 2 4 6 8 15 21 -21 -21 +1 +1 +1 +1 +1 +1 10 6 2 12 7 3 14 13 11 diff --git a/tests/aiger/gold/_xor_00000.aag b/tests/aiger/gold/_xor_00000.aag index 453b8d0ee..883df0907 100644 --- a/tests/aiger/gold/_xor_00000.aag +++ b/tests/aiger/gold/_xor_00000.aag @@ -1,12 +1,51 @@ -aag 8 5 0 1 3 +aag 40 16 0 8 24 2 4 6 8 10 +12 +14 16 -12 8 2 -14 9 3 -16 15 13 +18 +20 +22 +24 +26 +28 +30 +32 +38 +44 +50 +56 +62 +68 +74 +80 +34 18 2 +36 19 3 +38 37 35 +40 20 4 +42 21 5 +44 43 41 +46 22 6 +48 23 7 +50 49 47 +52 24 8 +54 25 9 +56 55 53 +58 26 10 +60 27 11 +62 61 59 +64 28 12 +66 29 13 +68 67 65 +70 30 14 +72 31 15 +74 73 71 +76 32 16 +78 33 17 +80 79 77 c Generated by Yosys