diff --git a/kernel/calc.cc b/kernel/calc.cc index 9b0885db9..589fd4aa7 100644 --- a/kernel/calc.cc +++ b/kernel/calc.cc @@ -658,6 +658,30 @@ RTLIL::Const RTLIL::const_bmux(const RTLIL::Const &arg1, const RTLIL::Const &arg return t; } +RTLIL::Const RTLIL::const_priority(const RTLIL::Const &arg, int p_width, const RTLIL::Const &polarity) +{ + std::vector t; + for (int offset = 0; offset < GetSize(arg); offset += p_width) + { + std::optional first_non_zero = std::nullopt; + for (int i = offset; i < offset + p_width; 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; + } + } + } + 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..0f15ca61d 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, cell->getParam(ID::P_WIDTH).as_int(), cell->getParam(ID::POLARITY)); + } + 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..e189d804d 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) @@ -614,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) @@ -678,6 +680,7 @@ X(PRODUCT_NEGATED) X(P_BYPASS) X(P_EN) X(P_SRST_N) +X(P_WIDTH) X(Q) X(QL_DSP2) X(R) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 0103cabfb..9c5e3069b 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -2654,6 +2654,15 @@ namespace { check_expected(); return; } + if (cell->type.in(ID($priority))) { + param(ID::WIDTH); + param(ID::P_WIDTH); + param(ID::POLARITY); + port(ID::A, param(ID::P_WIDTH)*param(ID::WIDTH)); + port(ID::Y, param(ID::P_WIDTH)*param(ID::WIDTH)); + check_expected(); + return; + } /* * Checklist for adding internal cell types * ======================================== @@ -3969,6 +3978,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) @@ -4528,7 +4545,8 @@ void RTLIL::Cell::check() void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) { if (!type.begins_with("$") || type.begins_with("$_") || type.begins_with("$paramod") || type.begins_with("$fmcombine") || - type.begins_with("$verific$") || type.begins_with("$array:") || type.begins_with("$extern:")) + type.begins_with("$verific$") || type.begins_with("$array:") || type.begins_with("$extern:")|| + type.begins_with("$priority")) return; if (type == ID($buf) || type == ID($mux) || type == ID($pmux) || type == ID($bmux) || type == ID($bwmux) || type == ID($bweqx)) { diff --git a/kernel/rtlil.h b/kernel/rtlil.h index fe280c965..9a809fdbe 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, int p_width, 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); @@ -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..c9c0fed4f 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -430,6 +430,47 @@ 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; + + const Const& polarity = cell->getParam(ID::POLARITY); + int p_width = cell->getParam(ID::P_WIDTH).as_int(); + + for (size_t offset = 0; offset < a.size(); offset += p_width) { + int any_previous_active; + if (p_width) { + any_previous_active = polarity[offset] ? a[offset] : ez->NOT(a[offset]); + ez->assume(ez->IFF(yy[offset], a[offset])); + } + for (size_t i = offset + 1; i < offset + p_width; 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 (p_width) { + any_previous_undef = undef_a[offset]; + ez->assume(ez->IFF(undef_y[offset], undef_a[offset])); + } + for (size_t i = offset + 1; i < offset + p_width; i++) { + 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); + } + } + + 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/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 06c740a88..0bd442e2e 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -17,15 +17,31 @@ * */ +#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" +#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) @@ -53,13 +69,23 @@ 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>; + +/** + * 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) { // 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,13 +107,190 @@ 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", 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; + // 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 set_pol; + std::optional reset_pol; + + for (auto it = async_rules.cbegin(); it != async_rules.cend(); it++) + { + const auto& [sync_value, rule] = *it; + for (int i = 0; i < sigs.d.size(); i++) { + 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)) { + // 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; + } + 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({}); + BitRule bit_rule {rule->signal[0], trig_pol, effect}; + bit_rules[i].push_back(bit_rule); + + 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) { + set_pol = trig_pol; + } else { + reset_pol = trig_pol; + } + } + } + + 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>; + 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); + 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"); +} + 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) { @@ -261,7 +464,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(mod, sigs, clk_pol, async_rules, ce, proc); continue; } @@ -305,6 +510,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); diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index 4d28e659b..a3faf3945 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -136,6 +136,32 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce cell->setPort(ID::Y, wire); } + if (cell_type == ID($priority)) + { + int priority_width = 1 + xorshift32(8 * bloat_factor); + int width = 1 + xorshift32(8 * bloat_factor); + int port_width = width * priority_width; + + wire = module->addWire(ID::A); + wire->width = port_width; + wire->port_input = true; + cell->setPort(ID::A, wire); + + wire = module->addWire(ID::Y); + wire->width = port_width; + wire->port_output = true; + cell->setPort(ID::Y, wire); + + RTLIL::SigSpec polarity; + for (int i = 0; i < port_width; i++) + polarity.append(xorshift32(2) ? State::S1 : State::S0); + + cell->setParam(ID::POLARITY, polarity.as_const()); + log("polarity: %s\n", log_signal(polarity)); + cell->setParam(ID::P_WIDTH, priority_width); + cell->setParam(ID::WIDTH, width); + } + if (cell_type == ID($fa)) { int width = 1 + xorshift32(8 * bloat_factor); @@ -1039,6 +1065,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..1c0e16735 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -3250,3 +3250,25 @@ 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 = 0; + parameter P_WIDTH = 0; + parameter POLARITY = 0; + input [P_WIDTH*WIDTH-1:0] A; + output [P_WIDTH*WIDTH-1:0] Y; + + genvar offset; + generate + for (offset = 0; offset < P_WIDTH*WIDTH; offset = offset + P_WIDTH) begin + assign Y[offset : offset+P_WIDTH-1] = POLARITY[offset : offset+P_WIDTH-1] ^ ((A[offset : offset+P_WIDTH-1] ^ POLARITY[offset : offset+P_WIDTH-1]) & (~(A[offset : offset+P_WIDTH-1] ^ POLARITY[offset : offset+P_WIDTH-1]) + 1)); + end + endgenerate +endmodule diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v index c3364e628..5b6665c92 100644 --- a/techlibs/common/techmap.v +++ b/techlibs/common/techmap.v @@ -679,3 +679,38 @@ 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 = 0; + parameter P_WIDTH = 0; + parameter POLARITY = 0; + + (* force_downto *) + input [P_WIDTH*WIDTH-1:0] A; + (* force_downto *) + output [P_WIDTH*WIDTH-1:0] Y; + + (* force_downto *) + wire [P_WIDTH*WIDTH-1:0] tmp; + (* force_downto *) + wire [P_WIDTH*WIDTH-1:0] A_active; + wire [P_WIDTH*WIDTH-1:0] Y_active; + assign A_active = A ^ ~POLARITY; + assign Y = Y_active ^ ~POLARITY; + + genvar i, offset; + generate + for (offset = 0; offset < P_WIDTH*WIDTH; offset = offset + P_WIDTH) begin + if (P_WIDTH > 0) begin + assign tmp[offset] = A_active[offset]; + assign Y_active[offset] = A_active[offset]; + end + for (i = offset + 1; i < offset + P_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 + end + endgenerate + +endmodule