3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-01-18 16:28:57 +00:00
This commit is contained in:
Emil J 2026-01-16 23:56:45 +01:00 committed by GitHub
commit 7e3e156f70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 392 additions and 8 deletions

View file

@ -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<State> t;
for (int offset = 0; offset < GetSize(arg); offset += p_width)
{
std::optional<State> 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);

View file

@ -118,7 +118,7 @@ struct CellTypes
void setup_internals_eval()
{
std::vector<RTLIL::IdString> 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;

View file

@ -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)

View file

@ -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)) {

View file

@ -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 = "");

View file

@ -430,6 +430,47 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
return true;
}
if (cell->type == ID($priority))
{
std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
std::vector<int> 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<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
std::vector<int> 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<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);

View file

@ -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 <optional>
#include <sstream>
#include <stdlib.h>
#include <stdio.h>
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::vector<BitRule>> {std::size_t operator()(const std::vector<BitRule>& 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<std::pair<RTLIL::SigSpec, RTLIL::SyncRule*>> &async_rules, RTLIL::Process *proc)
struct DSigs {
RTLIL::SigSpec d;
RTLIL::SigSpec q;
RTLIL::SigSpec clk;
};
using Rules = std::vector<std::pair<RTLIL::SigSpec, RTLIL::SyncRule*>>;
/**
* 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<std::vector<std::optional<BitRule>>> bit_rules(sigs.d.size());
// For checking consistent per-bit set/reset edges and bailing out on inconsistent
std::optional<bool> set_pol;
std::optional<bool> 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<SigBit>, std::optional<SigBit>>;
std::vector<RTLIL::Cell*> cells {};
std::unordered_map<std::vector<BitRule>, BitControl> map = {};
RTLIL::Module* mod;
RTLIL::Wire* prioritized;
RTLIL::SigSpec priority_in;
std::vector<RTLIL::State> 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<std::optional<BitRule>>& rules) {
std::vector<BitRule> 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<SigBit> 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<SigBit> 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);

View file

@ -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)

View file

@ -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

View file

@ -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