mirror of https://github.com/YosysHQ/yosys.git
Merge branch 'main' into opt_addcin
This commit is contained in:
commit
5c3fbd2d63
|
|
@ -1416,7 +1416,7 @@ struct HierarchyPass : public Pass {
|
|||
continue;
|
||||
}
|
||||
|
||||
Wire *t = module->addWire(NEW_ID, GetSize(c));
|
||||
Wire *t = module->addWire(NEW_ID2_SUFFIX("wand_wor"), GetSize(c));
|
||||
new_sig.append(t);
|
||||
update_port = true;
|
||||
|
||||
|
|
@ -1440,24 +1440,25 @@ struct HierarchyPass : public Pass {
|
|||
{
|
||||
bool wand = wand_map.count(w);
|
||||
SigSpec sigs = wand ? wand_map.at(w) : wor_map.at(w);
|
||||
IdString name = w->name;
|
||||
|
||||
if (GetSize(sigs) == 0)
|
||||
continue;
|
||||
|
||||
if (GetSize(w) == 1) {
|
||||
if (wand)
|
||||
module->addReduceAnd(NEW_ID, sigs, w);
|
||||
module->addReduceAnd(NEW_ID4_SUFFIX("reduce_and"), sigs, w);
|
||||
else
|
||||
module->addReduceOr(NEW_ID, sigs, w);
|
||||
module->addReduceOr(NEW_ID4_SUFFIX("reduce_or"), sigs, w);
|
||||
continue;
|
||||
}
|
||||
|
||||
SigSpec s = sigs.extract(0, GetSize(w));
|
||||
for (int i = GetSize(w); i < GetSize(sigs); i += GetSize(w)) {
|
||||
if (wand)
|
||||
s = module->And(NEW_ID, s, sigs.extract(i, GetSize(w)));
|
||||
s = module->And(NEW_ID4_SUFFIX("and"), s, sigs.extract(i, GetSize(w)));
|
||||
else
|
||||
s = module->Or(NEW_ID, s, sigs.extract(i, GetSize(w)));
|
||||
s = module->Or(NEW_ID4_SUFFIX("or"), s, sigs.extract(i, GetSize(w)));
|
||||
}
|
||||
module->connect(w, s);
|
||||
}
|
||||
|
|
@ -1519,7 +1520,7 @@ struct HierarchyPass : public Pass {
|
|||
if (w->port_input && !w->port_output)
|
||||
sig.extend_u0(GetSize(w), sig.is_wire() && sig.as_wire()->is_signed);
|
||||
else
|
||||
sig.append(module->addWire(NEW_ID, n));
|
||||
sig.append(module->addWire(NEW_ID2_SUFFIX("port_resize"), n));
|
||||
}
|
||||
|
||||
if (!conn.second.is_fully_const() || !w->port_input || w->port_output)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ OBJS += passes/opt/opt_lut_ins.o
|
|||
OBJS += passes/opt/opt_ffinv.o
|
||||
OBJS += passes/opt/pmux2shiftx.o
|
||||
OBJS += passes/opt/muxpack.o
|
||||
OBJS += passes/opt/opt_andor_pmux.o
|
||||
OBJS += passes/opt/opt_balance_tree.o
|
||||
OBJS += passes/opt/opt_parallel_prefix.o
|
||||
OBJS += passes/opt/opt_prienc.o
|
||||
|
|
|
|||
|
|
@ -0,0 +1,492 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHERWISE, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct OptAndOrPmuxWorker
|
||||
{
|
||||
struct DriverBit {
|
||||
Cell *cell = nullptr;
|
||||
IdString port;
|
||||
int index = -1;
|
||||
};
|
||||
|
||||
struct ConsumerBit {
|
||||
Cell *cell = nullptr;
|
||||
IdString port;
|
||||
int index = -1;
|
||||
};
|
||||
|
||||
struct EqInfo {
|
||||
SigSpec select;
|
||||
Const value;
|
||||
SigBit bit;
|
||||
};
|
||||
|
||||
struct DataExpr {
|
||||
std::vector<SigBit> factors;
|
||||
};
|
||||
|
||||
struct Contribution {
|
||||
EqInfo eq;
|
||||
DataExpr data;
|
||||
};
|
||||
|
||||
enum TermResult {
|
||||
TERM_FAIL,
|
||||
TERM_ZERO,
|
||||
TERM_OK,
|
||||
};
|
||||
|
||||
struct Arm {
|
||||
Const value;
|
||||
SigBit select_bit;
|
||||
std::vector<std::vector<DataExpr>> bits;
|
||||
};
|
||||
|
||||
Module *module;
|
||||
SigMap sigmap;
|
||||
|
||||
dict<SigBit, DriverBit> bit_drivers;
|
||||
dict<SigBit, std::vector<ConsumerBit>> bit_consumers;
|
||||
pool<Cell*> removed_cells;
|
||||
|
||||
int converted_count = 0;
|
||||
static const int max_cone_bits = 100000;
|
||||
|
||||
OptAndOrPmuxWorker(Module *module) : module(module), sigmap(module)
|
||||
{
|
||||
run();
|
||||
}
|
||||
|
||||
void build_maps()
|
||||
{
|
||||
bit_drivers.clear();
|
||||
bit_consumers.clear();
|
||||
|
||||
for (auto cell : module->cells())
|
||||
{
|
||||
for (auto &conn : cell->connections())
|
||||
{
|
||||
SigSpec sig = sigmap(conn.second);
|
||||
|
||||
if (cell->output(conn.first)) {
|
||||
for (int i = 0; i < GetSize(sig); i++) {
|
||||
SigBit bit = sig[i];
|
||||
if (bit.wire == nullptr)
|
||||
continue;
|
||||
if (bit_drivers.count(bit))
|
||||
bit_drivers[bit].cell = nullptr;
|
||||
else
|
||||
bit_drivers[bit] = {cell, conn.first, i};
|
||||
}
|
||||
}
|
||||
|
||||
if (cell->input(conn.first)) {
|
||||
for (int i = 0; i < GetSize(sig); i++) {
|
||||
SigBit bit = sig[i];
|
||||
if (bit.wire == nullptr)
|
||||
continue;
|
||||
bit_consumers[bit].push_back({cell, conn.first, i});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool get_driver(SigBit bit, DriverBit &driver) const
|
||||
{
|
||||
bit = sigmap(bit);
|
||||
auto it = bit_drivers.find(bit);
|
||||
if (it == bit_drivers.end() || it->second.cell == nullptr)
|
||||
return false;
|
||||
if (removed_cells.count(it->second.cell))
|
||||
return false;
|
||||
driver = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_input_bit(Cell *cell, IdString port, int index, SigBit &bit) const
|
||||
{
|
||||
SigSpec sig = sigmap(cell->getPort(port));
|
||||
if (index < 0 || index >= GetSize(sig))
|
||||
return false;
|
||||
bit = sig[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool feeds_or(Cell *cell) const
|
||||
{
|
||||
SigSpec y = sigmap(cell->getPort(ID::Y));
|
||||
for (auto bit : y) {
|
||||
if (bit.wire == nullptr)
|
||||
continue;
|
||||
auto it = bit_consumers.find(bit);
|
||||
if (it == bit_consumers.end())
|
||||
continue;
|
||||
for (auto &consumer : it->second)
|
||||
if (!removed_cells.count(consumer.cell) && consumer.cell != cell && consumer.cell->type == ID($or))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_observable_output(Cell *cell) const
|
||||
{
|
||||
SigSpec y = sigmap(cell->getPort(ID::Y));
|
||||
for (auto bit : y) {
|
||||
if (bit.wire == nullptr)
|
||||
continue;
|
||||
if (bit.wire->port_output || bit.wire->get_bool_attribute(ID::keep))
|
||||
return true;
|
||||
auto it = bit_consumers.find(bit);
|
||||
if (it != bit_consumers.end())
|
||||
for (auto &consumer : it->second)
|
||||
if (!removed_cells.count(consumer.cell))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool match_eq(SigBit bit, EqInfo &eq) const
|
||||
{
|
||||
DriverBit driver;
|
||||
if (!get_driver(bit, driver))
|
||||
return false;
|
||||
|
||||
Cell *cell = driver.cell;
|
||||
if (driver.port != ID::Y || driver.index != 0 || cell->type != ID($eq))
|
||||
return false;
|
||||
|
||||
SigSpec nonconst_sig = sigmap(cell->getPort(ID::A));
|
||||
SigSpec const_sig = sigmap(cell->getPort(ID::B));
|
||||
|
||||
if (!const_sig.is_fully_const()) {
|
||||
if (!nonconst_sig.is_fully_const())
|
||||
return false;
|
||||
std::swap(nonconst_sig, const_sig);
|
||||
}
|
||||
|
||||
if (nonconst_sig.empty() || const_sig.empty() || nonconst_sig.is_fully_const())
|
||||
return false;
|
||||
|
||||
eq.select = nonconst_sig;
|
||||
eq.value = const_sig.as_const();
|
||||
eq.bit = sigmap(bit);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool collect_or_terms(SigBit bit, std::vector<SigBit> &terms, pool<SigBit> &seen, int &budget) const
|
||||
{
|
||||
if (--budget < 0)
|
||||
return false;
|
||||
|
||||
bit = sigmap(bit);
|
||||
|
||||
if (bit == State::S0 || bit == State::Sx || bit == State::Sz)
|
||||
return true;
|
||||
if (bit == State::S1)
|
||||
return false;
|
||||
|
||||
if (!seen.insert(bit).second)
|
||||
return false;
|
||||
|
||||
DriverBit driver;
|
||||
if (get_driver(bit, driver) && driver.port == ID::Y && driver.cell->type == ID($or)) {
|
||||
SigBit a, b;
|
||||
if (!get_input_bit(driver.cell, ID::A, driver.index, a))
|
||||
return false;
|
||||
if (!get_input_bit(driver.cell, ID::B, driver.index, b))
|
||||
return false;
|
||||
return collect_or_terms(a, terms, seen, budget) && collect_or_terms(b, terms, seen, budget);
|
||||
}
|
||||
|
||||
terms.push_back(bit);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool collect_and_factors(SigBit bit, std::vector<SigBit> &factors, pool<SigBit> &seen, int &budget) const
|
||||
{
|
||||
if (--budget < 0)
|
||||
return false;
|
||||
|
||||
bit = sigmap(bit);
|
||||
|
||||
if (bit == State::S0 || bit == State::S1 || bit == State::Sx || bit == State::Sz) {
|
||||
factors.push_back(bit);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!seen.insert(bit).second)
|
||||
return false;
|
||||
|
||||
DriverBit driver;
|
||||
if (get_driver(bit, driver) && driver.port == ID::Y && driver.cell->type == ID($and)) {
|
||||
SigBit a, b;
|
||||
if (!get_input_bit(driver.cell, ID::A, driver.index, a))
|
||||
return false;
|
||||
if (!get_input_bit(driver.cell, ID::B, driver.index, b))
|
||||
return false;
|
||||
return collect_and_factors(a, factors, seen, budget) && collect_and_factors(b, factors, seen, budget);
|
||||
}
|
||||
|
||||
factors.push_back(bit);
|
||||
return true;
|
||||
}
|
||||
|
||||
TermResult parse_term(SigBit bit, Contribution &contrib) const
|
||||
{
|
||||
EqInfo direct_eq;
|
||||
if (match_eq(bit, direct_eq)) {
|
||||
contrib.eq = direct_eq;
|
||||
contrib.data.factors.clear();
|
||||
return TERM_OK;
|
||||
}
|
||||
|
||||
std::vector<SigBit> factors;
|
||||
pool<SigBit> seen;
|
||||
int budget = max_cone_bits;
|
||||
if (!collect_and_factors(bit, factors, seen, budget))
|
||||
return TERM_FAIL;
|
||||
|
||||
bool have_eq = false;
|
||||
for (auto factor : factors)
|
||||
{
|
||||
factor = sigmap(factor);
|
||||
if (factor == State::S0)
|
||||
return TERM_ZERO;
|
||||
if (factor == State::Sx || factor == State::Sz)
|
||||
return TERM_FAIL;
|
||||
if (factor == State::S1)
|
||||
continue;
|
||||
|
||||
EqInfo eq;
|
||||
if (match_eq(factor, eq)) {
|
||||
if (!have_eq) {
|
||||
contrib.eq = eq;
|
||||
have_eq = true;
|
||||
} else if (contrib.eq.select != eq.select || contrib.eq.value != eq.value) {
|
||||
return TERM_FAIL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
contrib.data.factors.push_back(factor);
|
||||
}
|
||||
|
||||
return have_eq ? TERM_OK : TERM_FAIL;
|
||||
}
|
||||
|
||||
SigBit make_and_tree(Cell *cell, const std::vector<SigBit> &factors, const std::string &src)
|
||||
{
|
||||
SigBit result = State::S1;
|
||||
|
||||
for (auto factor : factors)
|
||||
{
|
||||
factor = sigmap(factor);
|
||||
if (factor == State::S0 || factor == State::Sx || factor == State::Sz)
|
||||
return State::S0;
|
||||
if (factor == State::S1)
|
||||
continue;
|
||||
if (result == State::S1) {
|
||||
result = factor;
|
||||
continue;
|
||||
}
|
||||
|
||||
Wire *wire = module->addWire(NEW_ID2_SUFFIX("andor_pmux_data_and"), 1);
|
||||
module->addAnd(NEW_ID2_SUFFIX("andor_pmux_data_and"), result, factor, SigBit(wire), false, src);
|
||||
result = SigBit(wire);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SigBit make_or_tree(Cell *cell, const std::vector<SigBit> &terms, const std::string &src)
|
||||
{
|
||||
SigBit result = State::S0;
|
||||
|
||||
for (auto term : terms)
|
||||
{
|
||||
term = sigmap(term);
|
||||
if (term == State::S1)
|
||||
return State::S1;
|
||||
if (term == State::S0 || term == State::Sx || term == State::Sz)
|
||||
continue;
|
||||
if (result == State::S0) {
|
||||
result = term;
|
||||
continue;
|
||||
}
|
||||
|
||||
Wire *wire = module->addWire(NEW_ID2_SUFFIX("andor_pmux_data_or"), 1);
|
||||
module->addOr(NEW_ID2_SUFFIX("andor_pmux_data_or"), result, term, SigBit(wire), false, src);
|
||||
result = SigBit(wire);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SigBit emit_data_bit(Cell *cell, const std::vector<DataExpr> &exprs, const std::string &src)
|
||||
{
|
||||
std::vector<SigBit> terms;
|
||||
for (auto &expr : exprs) {
|
||||
SigBit term = make_and_tree(cell, expr.factors, src);
|
||||
if (term == State::S1)
|
||||
return State::S1;
|
||||
if (term != State::S0 && term != State::Sx && term != State::Sz)
|
||||
terms.push_back(term);
|
||||
}
|
||||
return make_or_tree(cell, terms, src);
|
||||
}
|
||||
|
||||
bool try_convert(Cell *cell)
|
||||
{
|
||||
if (removed_cells.count(cell))
|
||||
return false;
|
||||
if (cell->type != ID($or) || cell->get_bool_attribute(ID::keep))
|
||||
return false;
|
||||
if (feeds_or(cell) || !has_observable_output(cell))
|
||||
return false;
|
||||
|
||||
SigSpec y = sigmap(cell->getPort(ID::Y));
|
||||
int width = GetSize(y);
|
||||
if (width == 0)
|
||||
return false;
|
||||
|
||||
SigSpec select;
|
||||
std::vector<Arm> arms;
|
||||
dict<Const, int> arm_index;
|
||||
|
||||
for (int bit_idx = 0; bit_idx < width; bit_idx++)
|
||||
{
|
||||
std::vector<SigBit> terms;
|
||||
pool<SigBit> seen;
|
||||
int budget = max_cone_bits;
|
||||
if (!collect_or_terms(y[bit_idx], terms, seen, budget))
|
||||
return false;
|
||||
|
||||
for (auto term : terms)
|
||||
{
|
||||
Contribution contrib;
|
||||
TermResult result = parse_term(term, contrib);
|
||||
if (result == TERM_ZERO)
|
||||
continue;
|
||||
if (result == TERM_FAIL)
|
||||
return false;
|
||||
|
||||
if (select.empty())
|
||||
select = contrib.eq.select;
|
||||
else if (select != contrib.eq.select)
|
||||
return false;
|
||||
|
||||
int arm_idx;
|
||||
auto it = arm_index.find(contrib.eq.value);
|
||||
if (it == arm_index.end()) {
|
||||
arm_idx = GetSize(arms);
|
||||
arms.push_back({contrib.eq.value, contrib.eq.bit, std::vector<std::vector<DataExpr>>(width)});
|
||||
arm_index[contrib.eq.value] = arm_idx;
|
||||
} else {
|
||||
arm_idx = it->second;
|
||||
}
|
||||
|
||||
arms[arm_idx].bits[bit_idx].push_back(contrib.data);
|
||||
}
|
||||
}
|
||||
|
||||
if (GetSize(arms) < 2)
|
||||
return false;
|
||||
|
||||
SigSpec pmux_s, pmux_b;
|
||||
std::string src = cell->get_src_attribute();
|
||||
|
||||
for (auto &arm : arms)
|
||||
{
|
||||
SigSpec data;
|
||||
for (int bit_idx = 0; bit_idx < width; bit_idx++)
|
||||
data.append(emit_data_bit(cell, arm.bits[bit_idx], src));
|
||||
|
||||
pmux_b.append(data);
|
||||
pmux_s.append(arm.select_bit);
|
||||
}
|
||||
|
||||
log("Converting AND/OR mux %s.%s to a $pmux with %d cases and width %d.\n",
|
||||
log_id(module), log_id(cell), GetSize(arms), width);
|
||||
|
||||
module->addPmux(NEW_ID2_SUFFIX("andor_pmux"), Const(State::S0, width), pmux_b, pmux_s, cell->getPort(ID::Y), src);
|
||||
removed_cells.insert(cell);
|
||||
module->remove(cell);
|
||||
|
||||
converted_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
build_maps();
|
||||
|
||||
std::vector<Cell*> cells;
|
||||
for (auto cell : module->selected_cells())
|
||||
cells.push_back(cell);
|
||||
|
||||
for (auto cell : cells)
|
||||
try_convert(cell);
|
||||
}
|
||||
};
|
||||
|
||||
struct OptAndOrPmuxPass : public Pass {
|
||||
OptAndOrPmuxPass() : Pass("opt_andor_pmux", "convert equality-decoded AND/OR muxes to $pmux") { }
|
||||
|
||||
void help() override
|
||||
{
|
||||
log("\n");
|
||||
log(" opt_andor_pmux [selection]\n");
|
||||
log("\n");
|
||||
log("This pass converts logic of the form:\n");
|
||||
log("\n");
|
||||
log(" (sel == C0 & D0) | (sel == C1 & D1) | ...\n");
|
||||
log("\n");
|
||||
log("into $pmux cells. It only rewrites terms whose select conditions are\n");
|
||||
log("equality comparisons against distinct constants of the same select signal.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing OPT_ANDOR_PMUX pass (AND/OR muxes to $pmux).\n");
|
||||
|
||||
size_t argidx = 1;
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
int total_converted = 0;
|
||||
for (auto module : design->selected_modules()) {
|
||||
OptAndOrPmuxWorker worker(module);
|
||||
total_converted += worker.converted_count;
|
||||
}
|
||||
|
||||
if (total_converted)
|
||||
design->scratchpad_set_bool("opt.did_something", true);
|
||||
|
||||
log("Converted %d AND/OR muxes to $pmux cells.\n", total_converted);
|
||||
}
|
||||
} OptAndOrPmuxPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
@ -94,6 +94,7 @@ struct ModuleIndex {
|
|||
|
||||
bool apply_changes(ModuleIndex &parent, Cell *instantiation) {
|
||||
log_assert(instantiation->module == parent.module);
|
||||
Cell *cell = instantiation;
|
||||
|
||||
if (module->get_blackbox_attribute()) {
|
||||
// no propagating out of blackboxes
|
||||
|
|
@ -142,7 +143,7 @@ struct ModuleIndex {
|
|||
rhs.replace(constant_outputs);
|
||||
log_assert(rhs.is_fully_const());
|
||||
parent.module->connect(value.extract(chunk.offset, chunk.width), rhs);
|
||||
SigSpec dummy = parent.module->addWire(NEW_ID_SUFFIX("const_output"), chunk.width);
|
||||
SigSpec dummy = parent.module->addWire(NEW_ID2_SUFFIX("const_output"), chunk.width);
|
||||
for (int i = 0; i < chunk.width; i++)
|
||||
value[chunk.offset + i] = dummy[i];
|
||||
}
|
||||
|
|
@ -187,7 +188,7 @@ struct ModuleIndex {
|
|||
severed_port_bits.sort_and_unify();
|
||||
for (auto chunk : severed_port_bits.chunks()) {
|
||||
SigSpec &value = instantiation->connections_.at(chunk.wire->name);
|
||||
SigSpec dummy = parent.module->addWire(NEW_ID_SUFFIX("tie_together"), chunk.width);
|
||||
SigSpec dummy = parent.module->addWire(NEW_ID2_SUFFIX("tie_together"), chunk.width);
|
||||
for (int i = 0; i < chunk.width; i++)
|
||||
value[chunk.offset + i] = dummy[i];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ OBJS += passes/silimate/obs_clean.o
|
|||
OBJS += passes/silimate/segv.o
|
||||
OBJS += passes/silimate/reg_rename.o
|
||||
OBJS += passes/silimate/infer_ce.o
|
||||
OBJS += passes/silimate/ffnormpol.o
|
||||
OBJS += passes/silimate/report_fanout.o
|
||||
OBJS += passes/silimate/splitfanout.o
|
||||
OBJS += passes/silimate/splitlarge.o
|
||||
|
|
@ -20,6 +21,7 @@ OBJS += passes/silimate/cone_partition.o
|
|||
OBJS += passes/silimate/clkmerge.o
|
||||
OBJS += passes/silimate/opt_boundary.o
|
||||
OBJS += passes/silimate/opt_vps.o
|
||||
OBJS += passes/silimate/infer_icg.o
|
||||
|
||||
OBJS += passes/silimate/opt_expand.o
|
||||
GENFILES += passes/silimate/peepopt_expand.h
|
||||
|
|
|
|||
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2026 Silimate Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/ff.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct FfNormPolPass : public Pass {
|
||||
FfNormPolPass() : Pass("ffnormpol", "normalize FF/latch control polarities to positive") { }
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" ffnormpol [selection]\n");
|
||||
log("\n");
|
||||
log("This pass normalizes built-in FF and latch control polarities to\n");
|
||||
log("positive polarity. Negative-edge clocks and active-low enables,\n");
|
||||
log("resets, sets, clears, and latch enables are rewritten by inserting\n");
|
||||
log("inverters on the corresponding control signals and re-emitting the\n");
|
||||
log("cell with positive polarity.\n");
|
||||
log("\n");
|
||||
log("Both coarse-grain cells with polarity parameters and fine-grain cells\n");
|
||||
log("with polarity encoded in the cell type are handled through FfData.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
struct Worker {
|
||||
Module *module;
|
||||
SigMap sigmap;
|
||||
FfInitVals initvals;
|
||||
dict<SigSpec, SigSpec> inverted_signals;
|
||||
int normalized_cells = 0;
|
||||
int normalized_controls = 0;
|
||||
|
||||
Worker(Module *module) : module(module), sigmap(module), initvals(&sigmap, module) { }
|
||||
|
||||
SigSpec invert(SigSpec sig, const char *suffix, const std::string &src)
|
||||
{
|
||||
sig = sigmap(sig);
|
||||
if (inverted_signals.count(sig))
|
||||
return inverted_signals.at(sig);
|
||||
|
||||
SigSpec inv = module->Not(NEW_ID_SUFFIX(suffix), sig, false, src);
|
||||
inverted_signals[sig] = inv;
|
||||
return inv;
|
||||
}
|
||||
|
||||
bool normalize(FfData &ff, SigSpec &sig, bool &pol, const char *suffix)
|
||||
{
|
||||
if (pol)
|
||||
return false;
|
||||
|
||||
sig = invert(sig, suffix, ff.attributes[ID::src].decode_string());
|
||||
pol = true;
|
||||
normalized_controls++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
std::vector<Cell *> cells;
|
||||
for (auto cell : module->selected_cells())
|
||||
if (cell->is_builtin_ff())
|
||||
cells.push_back(cell);
|
||||
|
||||
for (auto cell : cells) {
|
||||
FfData ff(&initvals, cell);
|
||||
bool changed = false;
|
||||
|
||||
if (ff.has_clk)
|
||||
changed |= normalize(ff, ff.sig_clk, ff.pol_clk, "ffnormpol_clk");
|
||||
if (ff.has_ce)
|
||||
changed |= normalize(ff, ff.sig_ce, ff.pol_ce, "ffnormpol_ce");
|
||||
if (ff.has_aload)
|
||||
changed |= normalize(ff, ff.sig_aload, ff.pol_aload, "ffnormpol_aload");
|
||||
if (ff.has_arst)
|
||||
changed |= normalize(ff, ff.sig_arst, ff.pol_arst, "ffnormpol_arst");
|
||||
if (ff.has_srst)
|
||||
changed |= normalize(ff, ff.sig_srst, ff.pol_srst, "ffnormpol_srst");
|
||||
if (ff.has_sr) {
|
||||
changed |= normalize(ff, ff.sig_clr, ff.pol_clr, "ffnormpol_clr");
|
||||
changed |= normalize(ff, ff.sig_set, ff.pol_set, "ffnormpol_set");
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
ff.emit();
|
||||
normalized_cells++;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing FFNORMPOL pass (normalize FF/latch control polarities).\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
break;
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
int total_cells = 0;
|
||||
int total_controls = 0;
|
||||
for (auto module : design->selected_modules()) {
|
||||
Worker worker(module);
|
||||
worker.run();
|
||||
total_cells += worker.normalized_cells;
|
||||
total_controls += worker.normalized_controls;
|
||||
}
|
||||
|
||||
log("Normalized %d controls on %d FF/latch cells.\n", total_controls, total_cells);
|
||||
}
|
||||
} FfNormPolPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2026 Silimate Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct InferIcgPass : public Pass {
|
||||
InferIcgPass() : Pass("infer_icg", "infer $icg cells from latch-based clock gates") { }
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" infer_icg [selection]\n");
|
||||
log("\n");
|
||||
log("This pass recognizes latch-based integrated clock gate structures and\n");
|
||||
log("replaces the gated-clock output with an internal $icg cell.\n");
|
||||
log("\n");
|
||||
log("The recognized positive-edge pattern is a one-bit $dlatch that is\n");
|
||||
log("transparent while the clock is low, feeding a one-bit $and or $logic_and\n");
|
||||
log("with the same clock. The latch can be active-low on the clock or\n");
|
||||
log("active-high on an inverted copy of the clock.\n");
|
||||
log("\n");
|
||||
log("The complementary high-idle pattern is also recognized: a latch that is\n");
|
||||
log("transparent while the clock is high, feeding a one-bit $or or $logic_or\n");
|
||||
log("with the same clock and an inverted latched enable. This is rewritten as\n");
|
||||
log("a $icg on the inverted clock plus an output inverter.\n");
|
||||
log("\n");
|
||||
log("The scan-enable leg is optional. If the effective latch data is driven\n");
|
||||
log("by a one-bit $or or $logic_or, its inputs are used as the $icg EN and\n");
|
||||
log("SE ports; otherwise the data is used as EN and SE is tied low.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
static bool is_one_bit_binary(Cell *cell)
|
||||
{
|
||||
return cell->getParam(ID::A_WIDTH).as_int() == 1 &&
|
||||
cell->getParam(ID::B_WIDTH).as_int() == 1 &&
|
||||
cell->getParam(ID::Y_WIDTH).as_int() == 1;
|
||||
}
|
||||
|
||||
static bool is_and(Cell *cell)
|
||||
{
|
||||
return cell->type.in(ID($and), ID($logic_and)) && is_one_bit_binary(cell);
|
||||
}
|
||||
|
||||
static bool is_or(Cell *cell)
|
||||
{
|
||||
return cell->type.in(ID($or), ID($logic_or)) && is_one_bit_binary(cell);
|
||||
}
|
||||
|
||||
static bool is_not(Cell *cell)
|
||||
{
|
||||
if (cell->type == ID($_NOT_))
|
||||
return true;
|
||||
return cell->type.in(ID($not), ID($logic_not)) &&
|
||||
cell->getParam(ID::A_WIDTH).as_int() == 1 &&
|
||||
cell->getParam(ID::Y_WIDTH).as_int() == 1;
|
||||
}
|
||||
|
||||
static void add_driver(dict<SigBit, Cell *> &drivers, pool<SigBit> &multi_driver_bits, SigMap &sigmap, Cell *cell)
|
||||
{
|
||||
for (auto &conn : cell->connections()) {
|
||||
if (!cell->output(conn.first))
|
||||
continue;
|
||||
for (auto bit : sigmap(conn.second)) {
|
||||
if (bit.is_wire()) {
|
||||
if (drivers.count(bit) && drivers.at(bit) != cell)
|
||||
multi_driver_bits.insert(bit);
|
||||
else
|
||||
drivers[bit] = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool get_driver(dict<SigBit, Cell *> &drivers, pool<SigBit> &multi_driver_bits, SigMap &sigmap, SigSpec sig, Cell *&driver)
|
||||
{
|
||||
sig = sigmap(sig);
|
||||
if (GetSize(sig) != 1 || !sig[0].is_wire() || multi_driver_bits.count(sig[0]) || !drivers.count(sig[0]))
|
||||
return false;
|
||||
driver = drivers.at(sig[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool same_sig(SigMap &sigmap, SigSpec a, SigSpec b)
|
||||
{
|
||||
return sigmap(a) == sigmap(b);
|
||||
}
|
||||
|
||||
static bool get_not_input(dict<SigBit, Cell *> &drivers, pool<SigBit> &multi_driver_bits, SigMap &sigmap,
|
||||
SigSpec sig, SigSpec &input, Cell *¬_cell)
|
||||
{
|
||||
Cell *driver = nullptr;
|
||||
if (!get_driver(drivers, multi_driver_bits, sigmap, sig, driver) || !is_not(driver))
|
||||
return false;
|
||||
|
||||
not_cell = driver;
|
||||
input = sigmap(driver->getPort(ID::A));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool latch_transparent_when(Cell *latch, SigSpec clk, bool high_when_transparent,
|
||||
dict<SigBit, Cell *> &drivers, pool<SigBit> &multi_driver_bits, SigMap &sigmap)
|
||||
{
|
||||
SigSpec latch_en = sigmap(latch->getPort(ID::EN));
|
||||
bool latch_en_pol = latch->getParam(ID::EN_POLARITY).as_bool();
|
||||
|
||||
if (same_sig(sigmap, latch_en, clk))
|
||||
return latch_en_pol == high_when_transparent;
|
||||
|
||||
SigSpec inv_input;
|
||||
Cell *not_cell = nullptr;
|
||||
if (get_not_input(drivers, multi_driver_bits, sigmap, latch_en, inv_input, not_cell) &&
|
||||
same_sig(sigmap, inv_input, clk))
|
||||
return latch_en_pol != high_when_transparent;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing INFER_ICG pass (infer $icg cells from latch-based clock gates).\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
break;
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
int total_count = 0;
|
||||
|
||||
for (auto module : design->selected_modules()) {
|
||||
SigMap sigmap(module);
|
||||
dict<SigBit, Cell *> drivers;
|
||||
pool<SigBit> multi_driver_bits;
|
||||
dict<SigBit, int> users;
|
||||
pool<Cell *> selected_cells;
|
||||
|
||||
for (auto wire : module->wires())
|
||||
if (wire->port_output)
|
||||
for (auto bit : sigmap(wire))
|
||||
if (bit.is_wire())
|
||||
users[bit]++;
|
||||
|
||||
for (auto cell : module->cells()) {
|
||||
if (design->selected(module, cell))
|
||||
selected_cells.insert(cell);
|
||||
|
||||
add_driver(drivers, multi_driver_bits, sigmap, cell);
|
||||
|
||||
for (auto &conn : cell->connections()) {
|
||||
if (!cell->input(conn.first))
|
||||
continue;
|
||||
for (auto bit : sigmap(conn.second))
|
||||
if (bit.is_wire())
|
||||
users[bit]++;
|
||||
}
|
||||
}
|
||||
|
||||
struct Match {
|
||||
Cell *gate_cell;
|
||||
Cell *latch;
|
||||
Cell *enable_or_cell;
|
||||
SigSpec clk;
|
||||
SigSpec en;
|
||||
SigSpec se;
|
||||
SigSpec gclk;
|
||||
SigSpec latched_en;
|
||||
bool invert_clk_and_gclk;
|
||||
};
|
||||
std::vector<Match> matches;
|
||||
pool<Cell *> consumed_gate_cells;
|
||||
|
||||
for (auto gate_cell : module->selected_cells()) {
|
||||
if (consumed_gate_cells.count(gate_cell) || (!is_and(gate_cell) && !is_or(gate_cell)))
|
||||
continue;
|
||||
|
||||
bool gate_is_and = is_and(gate_cell);
|
||||
SigSpec gate_a = sigmap(gate_cell->getPort(ID::A));
|
||||
SigSpec gate_b = sigmap(gate_cell->getPort(ID::B));
|
||||
SigSpec gclk = gate_cell->getPort(ID::Y);
|
||||
|
||||
for (int clk_port = 0; clk_port < 2; clk_port++) {
|
||||
SigSpec clk = clk_port == 0 ? gate_a : gate_b;
|
||||
SigSpec latched_en = clk_port == 0 ? gate_b : gate_a;
|
||||
Cell *latch = nullptr;
|
||||
bool invert_clk_and_gclk = false;
|
||||
|
||||
if (!gate_is_and) {
|
||||
SigSpec not_input;
|
||||
Cell *not_cell = nullptr;
|
||||
if (!get_not_input(drivers, multi_driver_bits, sigmap, latched_en, not_input, not_cell))
|
||||
continue;
|
||||
latched_en = not_input;
|
||||
invert_clk_and_gclk = true;
|
||||
}
|
||||
|
||||
if (!get_driver(drivers, multi_driver_bits, sigmap, latched_en, latch))
|
||||
continue;
|
||||
if (!selected_cells.count(latch) || latch->type != ID($dlatch))
|
||||
continue;
|
||||
if (latch->getParam(ID::WIDTH).as_int() != 1)
|
||||
continue;
|
||||
|
||||
if (!latch_transparent_when(latch, clk, !gate_is_and, drivers, multi_driver_bits, sigmap))
|
||||
continue;
|
||||
|
||||
SigSpec latch_d = sigmap(latch->getPort(ID::D));
|
||||
SigSpec en = latch_d, se = State::S0;
|
||||
Cell *enable_or_cell = nullptr;
|
||||
|
||||
if (get_driver(drivers, multi_driver_bits, sigmap, latch_d, enable_or_cell) &&
|
||||
selected_cells.count(enable_or_cell) && is_or(enable_or_cell)) {
|
||||
en = sigmap(enable_or_cell->getPort(ID::A));
|
||||
se = sigmap(enable_or_cell->getPort(ID::B));
|
||||
} else {
|
||||
enable_or_cell = nullptr;
|
||||
}
|
||||
|
||||
matches.push_back({gate_cell, latch, enable_or_cell, clk, en, se, gclk, latched_en, invert_clk_and_gclk});
|
||||
consumed_gate_cells.insert(gate_cell);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &match : matches) {
|
||||
Cell *cell = match.gate_cell;
|
||||
Cell *icg = module->addCell(NEW_ID2_SUFFIX("icg"), ID($icg));
|
||||
SigSpec icg_clk = match.clk;
|
||||
SigSpec icg_gclk = match.gclk;
|
||||
|
||||
if (match.invert_clk_and_gclk) {
|
||||
icg_clk = module->Not(NEW_ID2_SUFFIX("icg_clk_n"), match.clk);
|
||||
icg_gclk = module->addWire(NEW_ID2_SUFFIX("icg_gclk"));
|
||||
}
|
||||
|
||||
icg->setPort(ID::CLK, icg_clk);
|
||||
icg->setPort(ID::EN, match.en);
|
||||
icg->setPort(ID::SE, match.se);
|
||||
icg->setPort(ID::GCLK, icg_gclk);
|
||||
icg->add_strpool_attribute(ID::src, match.gate_cell->get_strpool_attribute(ID::src));
|
||||
icg->add_strpool_attribute(ID::src, match.latch->get_strpool_attribute(ID::src));
|
||||
if (match.enable_or_cell)
|
||||
icg->add_strpool_attribute(ID::src, match.enable_or_cell->get_strpool_attribute(ID::src));
|
||||
|
||||
if (match.invert_clk_and_gclk)
|
||||
module->addNot(NEW_ID2_SUFFIX("icg_gclk_n"), icg_gclk, match.gclk, false, match.gate_cell->get_src_attribute());
|
||||
|
||||
log("Inferred $icg cell %s.%s from latch %s and gate %s.\n",
|
||||
log_id(module), log_id(icg), log_id(match.latch), log_id(match.gate_cell));
|
||||
|
||||
module->remove(match.gate_cell);
|
||||
|
||||
SigSpec latched_en = sigmap(match.latched_en);
|
||||
if (GetSize(latched_en) == 1 && latched_en[0].is_wire() && users[latched_en[0]] == 1) {
|
||||
module->remove(match.latch);
|
||||
|
||||
if (match.enable_or_cell) {
|
||||
SigSpec or_y = sigmap(match.enable_or_cell->getPort(ID::Y));
|
||||
if (GetSize(or_y) == 1 && or_y[0].is_wire() && users[or_y[0]] == 1)
|
||||
module->remove(match.enable_or_cell);
|
||||
}
|
||||
}
|
||||
|
||||
total_count++;
|
||||
}
|
||||
}
|
||||
|
||||
log("Inferred %d $icg cells.\n", total_count);
|
||||
}
|
||||
} InferIcgPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
log -header "Normalize coarse and fine FF/latch polarities"
|
||||
log -push
|
||||
design -reset
|
||||
read_rtlil <<EOT
|
||||
module \top
|
||||
wire input 1 \clk
|
||||
wire input 2 \en
|
||||
wire input 3 \arst
|
||||
wire input 4 \srst
|
||||
wire input 5 \aload
|
||||
wire width 2 input 6 \set
|
||||
wire width 2 input 7 \clr
|
||||
wire width 2 input 8 \d
|
||||
wire width 2 input 9 \ad
|
||||
wire width 2 output 10 \q_dffe
|
||||
wire width 2 output 11 \q_adffe
|
||||
wire width 2 output 12 \q_sdffce
|
||||
wire width 2 output 13 \q_aldffe
|
||||
wire width 2 output 14 \q_dlatchsr
|
||||
wire width 2 output 15 \q_sr
|
||||
wire output 16 \q_fine
|
||||
|
||||
cell $dffe \dffe_neg
|
||||
parameter \CLK_POLARITY 1'0
|
||||
parameter \EN_POLARITY 1'0
|
||||
parameter \WIDTH 2
|
||||
connect \CLK \clk
|
||||
connect \EN \en
|
||||
connect \D \d
|
||||
connect \Q \q_dffe
|
||||
end
|
||||
|
||||
cell $adffe \adffe_neg
|
||||
parameter \CLK_POLARITY 1'0
|
||||
parameter \EN_POLARITY 1'0
|
||||
parameter \ARST_POLARITY 1'0
|
||||
parameter \ARST_VALUE 2'01
|
||||
parameter \WIDTH 2
|
||||
connect \CLK \clk
|
||||
connect \EN \en
|
||||
connect \ARST \arst
|
||||
connect \D \d
|
||||
connect \Q \q_adffe
|
||||
end
|
||||
|
||||
cell $sdffce \sdffce_neg
|
||||
parameter \CLK_POLARITY 1'0
|
||||
parameter \EN_POLARITY 1'0
|
||||
parameter \SRST_POLARITY 1'0
|
||||
parameter \SRST_VALUE 2'10
|
||||
parameter \WIDTH 2
|
||||
connect \CLK \clk
|
||||
connect \EN \en
|
||||
connect \SRST \srst
|
||||
connect \D \d
|
||||
connect \Q \q_sdffce
|
||||
end
|
||||
|
||||
cell $aldffe \aldffe_neg
|
||||
parameter \CLK_POLARITY 1'0
|
||||
parameter \EN_POLARITY 1'0
|
||||
parameter \ALOAD_POLARITY 1'0
|
||||
parameter \WIDTH 2
|
||||
connect \CLK \clk
|
||||
connect \EN \en
|
||||
connect \ALOAD \aload
|
||||
connect \D \d
|
||||
connect \AD \ad
|
||||
connect \Q \q_aldffe
|
||||
end
|
||||
|
||||
cell $dlatchsr \dlatchsr_neg
|
||||
parameter \EN_POLARITY 1'0
|
||||
parameter \SET_POLARITY 1'0
|
||||
parameter \CLR_POLARITY 1'0
|
||||
parameter \WIDTH 2
|
||||
connect \EN \aload
|
||||
connect \SET \set
|
||||
connect \CLR \clr
|
||||
connect \D \d
|
||||
connect \Q \q_dlatchsr
|
||||
end
|
||||
|
||||
cell $sr \sr_neg
|
||||
parameter \SET_POLARITY 1'0
|
||||
parameter \CLR_POLARITY 1'0
|
||||
parameter \WIDTH 2
|
||||
connect \SET \set
|
||||
connect \CLR \clr
|
||||
connect \Q \q_sr
|
||||
end
|
||||
|
||||
cell $_DFFE_NN_ \fine_dffe_neg
|
||||
connect \C \clk
|
||||
connect \E \en
|
||||
connect \D \d [0]
|
||||
connect \Q \q_fine
|
||||
end
|
||||
end
|
||||
EOT
|
||||
|
||||
ffnormpol
|
||||
check -assert
|
||||
|
||||
select -assert-count 0 r:CLK_POLARITY=0
|
||||
select -assert-count 0 r:EN_POLARITY=0
|
||||
select -assert-count 0 r:ARST_POLARITY=0
|
||||
select -assert-count 0 r:SRST_POLARITY=0
|
||||
select -assert-count 0 r:ALOAD_POLARITY=0
|
||||
select -assert-count 0 r:SET_POLARITY=0
|
||||
select -assert-count 0 r:CLR_POLARITY=0
|
||||
select -assert-count 1 t:$_DFFE_PP_
|
||||
select -assert-count 7 t:$not
|
||||
|
||||
design -reset
|
||||
log -pop
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
log -header "Infer $icg from latch-based clock gate with scan enable"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top(input logic CK, EN, SE, output logic Q);
|
||||
logic en_ff;
|
||||
logic enable;
|
||||
|
||||
assign enable = EN | SE;
|
||||
|
||||
always_latch
|
||||
if (!CK)
|
||||
en_ff = enable;
|
||||
|
||||
assign Q = CK & en_ff;
|
||||
endmodule
|
||||
EOF
|
||||
|
||||
hierarchy -auto-top
|
||||
proc
|
||||
opt
|
||||
select -assert-count 1 t:$dlatch
|
||||
select -assert-count 1 t:$and t:$logic_and
|
||||
select -assert-count 1 t:$or t:$logic_or
|
||||
select -assert-count 0 t:$icg
|
||||
|
||||
infer_icg
|
||||
clean
|
||||
check -assert
|
||||
|
||||
select -assert-count 1 t:$icg
|
||||
select -assert-count 0 t:$dlatch
|
||||
select -assert-count 0 t:$and t:$logic_and
|
||||
select -assert-count 0 t:$or t:$logic_or
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Infer $icg from latch-based clock gate without scan enable"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top(input logic CK, EN, output logic Q);
|
||||
logic en_ff;
|
||||
|
||||
always_latch
|
||||
if (!CK)
|
||||
en_ff = EN;
|
||||
|
||||
assign Q = en_ff & CK;
|
||||
endmodule
|
||||
EOF
|
||||
|
||||
hierarchy -auto-top
|
||||
proc
|
||||
opt
|
||||
infer_icg
|
||||
clean
|
||||
check -assert
|
||||
|
||||
select -assert-count 1 t:$icg
|
||||
select -assert-count 0 t:$dlatch
|
||||
select -assert-count 0 t:$and t:$logic_and
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Infer $icg from active-high latch on inverted clock"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top(input logic CK, EN, SE, output logic Q);
|
||||
logic en_ff;
|
||||
logic enable;
|
||||
logic nCK;
|
||||
|
||||
assign enable = EN | SE;
|
||||
assign nCK = !CK;
|
||||
|
||||
always_latch
|
||||
if (nCK)
|
||||
en_ff = enable;
|
||||
|
||||
assign Q = CK & en_ff;
|
||||
endmodule
|
||||
EOF
|
||||
|
||||
hierarchy -auto-top
|
||||
proc
|
||||
opt
|
||||
infer_icg
|
||||
clean
|
||||
check -assert
|
||||
|
||||
select -assert-count 1 t:$icg
|
||||
select -assert-count 0 t:$dlatch
|
||||
select -assert-count 0 t:$and t:$logic_and
|
||||
select -assert-count 0 t:$or t:$logic_or
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Infer $icg from active-high latch on inverted clock without scan enable"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top(input logic CK, EN, output logic Q);
|
||||
logic en_ff;
|
||||
logic nCK;
|
||||
|
||||
assign nCK = !CK;
|
||||
|
||||
always_latch
|
||||
if (nCK)
|
||||
en_ff = EN;
|
||||
|
||||
assign Q = CK & en_ff;
|
||||
endmodule
|
||||
EOF
|
||||
|
||||
hierarchy -auto-top
|
||||
proc
|
||||
opt
|
||||
infer_icg
|
||||
clean
|
||||
check -assert
|
||||
|
||||
select -assert-count 1 t:$icg
|
||||
select -assert-count 0 t:$dlatch
|
||||
select -assert-count 0 t:$and t:$logic_and
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Infer $icg from high-idle OR clock gate"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top(input logic CK, EN, SE, output logic Q);
|
||||
logic en_ff;
|
||||
logic enable;
|
||||
|
||||
assign enable = EN | SE;
|
||||
|
||||
always_latch
|
||||
if (CK)
|
||||
en_ff = enable;
|
||||
|
||||
assign Q = CK | !en_ff;
|
||||
endmodule
|
||||
EOF
|
||||
|
||||
hierarchy -auto-top
|
||||
proc
|
||||
opt
|
||||
infer_icg
|
||||
clean
|
||||
check -assert
|
||||
|
||||
select -assert-count 1 t:$icg
|
||||
select -assert-count 0 t:$dlatch
|
||||
select -assert-count 0 t:$or t:$logic_or
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
log -header "Infer $icg from high-idle OR clock gate without scan enable"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top(input logic CK, EN, output logic Q);
|
||||
logic en_ff;
|
||||
|
||||
always_latch
|
||||
if (CK)
|
||||
en_ff = EN;
|
||||
|
||||
assign Q = CK | !en_ff;
|
||||
endmodule
|
||||
EOF
|
||||
|
||||
hierarchy -auto-top
|
||||
proc
|
||||
opt
|
||||
infer_icg
|
||||
clean
|
||||
check -assert
|
||||
|
||||
select -assert-count 1 t:$icg
|
||||
select -assert-count 0 t:$dlatch
|
||||
select -assert-count 0 t:$or t:$logic_or
|
||||
design -reset
|
||||
log -pop
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
module andor_pmux_basic (
|
||||
input [2:0] sel,
|
||||
input [5:0] d,
|
||||
input a,
|
||||
output [1:0] y
|
||||
);
|
||||
assign y = ({2{sel == 3'd1}} & d[1:0]) |
|
||||
({2{sel == 3'd3}} & {d[2] & a, d[3]}) |
|
||||
({2{sel == 3'd6}} & 2'b01);
|
||||
endmodule
|
||||
|
||||
module andor_pmux_outer_enable (
|
||||
input [2:0] sel,
|
||||
input [3:0] d,
|
||||
input en,
|
||||
output [1:0] y
|
||||
);
|
||||
wire [1:0] body;
|
||||
|
||||
assign body = ({2{sel == 3'd2}} & {1'b0, d[0]}) |
|
||||
({2{sel == 3'd5}} & {d[1], d[2]}) |
|
||||
({2{sel == 3'd7}} & {d[3], 1'b1});
|
||||
assign y = {2{en}} & body;
|
||||
endmodule
|
||||
|
||||
module andor_pmux_duplicate (
|
||||
input [1:0] sel,
|
||||
input a,
|
||||
input b,
|
||||
input c,
|
||||
input d,
|
||||
input e,
|
||||
input f,
|
||||
output [1:0] y
|
||||
);
|
||||
assign y = ({2{sel == 2'd1}} & {a, b}) |
|
||||
({2{sel == 2'd1}} & {c, d}) |
|
||||
({2{sel == 2'd2}} & {e, f});
|
||||
endmodule
|
||||
|
||||
module andor_pmux_mixed_select_negative (
|
||||
input [1:0] sel_a,
|
||||
input [1:0] sel_b,
|
||||
input a,
|
||||
input b,
|
||||
output y
|
||||
);
|
||||
assign y = ((sel_a == 2'd1) & a) |
|
||||
((sel_b == 2'd2) & b);
|
||||
endmodule
|
||||
|
||||
module andor_pmux_wide_decode (
|
||||
input [3:0] sel,
|
||||
input [23:0] d,
|
||||
input q,
|
||||
output [3:0] y
|
||||
);
|
||||
assign y = ({4{sel == 4'd1}} & d[3:0]) |
|
||||
({4{sel == 4'd4}} & {d[4] & q, d[5], 1'b0, d[6]}) |
|
||||
({4{sel == 4'd7}} & d[10:7]) |
|
||||
({4{sel == 4'd9}} & {1'b1, d[11], d[12] & q, d[13]}) |
|
||||
({4{sel == 4'd12}} & d[17:14]) |
|
||||
({4{sel == 4'd15}} & {d[18], d[19], d[20], 1'b1});
|
||||
endmodule
|
||||
|
||||
module andor_pmux_shared_subtree (
|
||||
input [2:0] sel,
|
||||
input [3:0] d,
|
||||
input q,
|
||||
output y,
|
||||
output z
|
||||
);
|
||||
wire sub = ((sel == 3'd1) & d[0]) |
|
||||
((sel == 3'd3) & d[1]);
|
||||
|
||||
assign y = sub | ((sel == 3'd6) & d[2]);
|
||||
assign z = sub & q;
|
||||
endmodule
|
||||
|
||||
module andor_pmux_single_arm_negative (
|
||||
input [1:0] sel,
|
||||
input [1:0] d,
|
||||
output [1:0] y
|
||||
);
|
||||
assign y = ({2{sel == 2'd1}} & d) | 2'b00;
|
||||
endmodule
|
||||
|
||||
module andor_pmux_all_zero_negative (
|
||||
input [1:0] sel,
|
||||
output [1:0] y
|
||||
);
|
||||
assign y = ({2{sel == 2'd1}} & 2'b00) |
|
||||
({2{sel == 2'd2}} & 2'b00);
|
||||
endmodule
|
||||
|
||||
module andor_pmux_non_eq_leaf_negative (
|
||||
input [1:0] sel,
|
||||
input raw,
|
||||
input a,
|
||||
input b,
|
||||
output y
|
||||
);
|
||||
assign y = ((sel == 2'd1) & a) |
|
||||
(raw & b);
|
||||
endmodule
|
||||
|
||||
module andor_pmux_duplicate_complex (
|
||||
input [2:0] sel,
|
||||
input [8:0] d,
|
||||
input q,
|
||||
input r,
|
||||
output [2:0] y
|
||||
);
|
||||
assign y = ({3{sel == 3'd2}} & {d[0] & q, d[1], d[2]}) |
|
||||
({3{sel == 3'd2}} & {d[3], d[4] & r, d[5]}) |
|
||||
({3{sel == 3'd5}} & {d[6], d[7] & q, d[8] & r});
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
read_verilog opt_andor_pmux.v
|
||||
design -save read
|
||||
|
||||
design -load read
|
||||
hierarchy -top andor_pmux_basic
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save gold
|
||||
opt_andor_pmux
|
||||
opt_clean
|
||||
select -assert-count 1 t:$pmux
|
||||
design -stash gate
|
||||
design -import gold -as gold
|
||||
design -import gate -as gate
|
||||
miter -equiv -flatten -make_assert -make_outputs gold gate miter
|
||||
sat -verify -prove-asserts -show-ports miter
|
||||
|
||||
design -load read
|
||||
hierarchy -top andor_pmux_wide_decode
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save gold
|
||||
opt_andor_pmux
|
||||
opt_clean
|
||||
select -assert-count 1 t:$pmux
|
||||
design -stash gate
|
||||
design -import gold -as gold
|
||||
design -import gate -as gate
|
||||
miter -equiv -flatten -make_assert -make_outputs gold gate miter
|
||||
sat -verify -prove-asserts -show-ports miter
|
||||
|
||||
design -load read
|
||||
hierarchy -top andor_pmux_shared_subtree
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save gold
|
||||
opt_andor_pmux
|
||||
opt_clean
|
||||
select -assert-count 1 t:$pmux
|
||||
design -stash gate
|
||||
design -import gold -as gold
|
||||
design -import gate -as gate
|
||||
miter -equiv -flatten -make_assert -make_outputs gold gate miter
|
||||
sat -verify -prove-asserts -show-ports miter
|
||||
|
||||
design -load read
|
||||
hierarchy -top andor_pmux_single_arm_negative
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save gold
|
||||
opt_andor_pmux
|
||||
opt_clean
|
||||
select -assert-count 0 t:$pmux
|
||||
design -stash gate
|
||||
design -import gold -as gold
|
||||
design -import gate -as gate
|
||||
miter -equiv -flatten -make_assert -make_outputs gold gate miter
|
||||
sat -verify -prove-asserts -show-ports miter
|
||||
|
||||
design -load read
|
||||
hierarchy -top andor_pmux_all_zero_negative
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save gold
|
||||
opt_andor_pmux
|
||||
opt_clean
|
||||
select -assert-count 0 t:$pmux
|
||||
design -stash gate
|
||||
design -import gold -as gold
|
||||
design -import gate -as gate
|
||||
miter -equiv -flatten -make_assert -make_outputs gold gate miter
|
||||
sat -verify -prove-asserts -show-ports miter
|
||||
|
||||
design -load read
|
||||
hierarchy -top andor_pmux_non_eq_leaf_negative
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save gold
|
||||
opt_andor_pmux
|
||||
opt_clean
|
||||
select -assert-count 0 t:$pmux
|
||||
design -stash gate
|
||||
design -import gold -as gold
|
||||
design -import gate -as gate
|
||||
miter -equiv -flatten -make_assert -make_outputs gold gate miter
|
||||
sat -verify -prove-asserts -show-ports miter
|
||||
|
||||
design -load read
|
||||
hierarchy -top andor_pmux_duplicate_complex
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save gold
|
||||
opt_andor_pmux
|
||||
opt_clean
|
||||
select -assert-count 1 t:$pmux
|
||||
design -stash gate
|
||||
design -import gold -as gold
|
||||
design -import gate -as gate
|
||||
miter -equiv -flatten -make_assert -make_outputs gold gate miter
|
||||
sat -verify -prove-asserts -show-ports miter
|
||||
|
||||
design -load read
|
||||
hierarchy -top andor_pmux_outer_enable
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save gold
|
||||
opt_andor_pmux
|
||||
opt_clean
|
||||
select -assert-count 1 t:$pmux
|
||||
design -stash gate
|
||||
design -import gold -as gold
|
||||
design -import gate -as gate
|
||||
miter -equiv -flatten -make_assert -make_outputs gold gate miter
|
||||
sat -verify -prove-asserts -show-ports miter
|
||||
|
||||
design -load read
|
||||
hierarchy -top andor_pmux_duplicate
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save gold
|
||||
opt_andor_pmux
|
||||
opt_clean
|
||||
select -assert-count 1 t:$pmux
|
||||
design -stash gate
|
||||
design -import gold -as gold
|
||||
design -import gate -as gate
|
||||
miter -equiv -flatten -make_assert -make_outputs gold gate miter
|
||||
sat -verify -prove-asserts -show-ports miter
|
||||
|
||||
design -load read
|
||||
hierarchy -top andor_pmux_mixed_select_negative
|
||||
proc
|
||||
opt_expr
|
||||
opt_clean
|
||||
design -save gold
|
||||
opt_andor_pmux
|
||||
opt_clean
|
||||
select -assert-count 0 t:$pmux
|
||||
design -stash gate
|
||||
design -import gold -as gold
|
||||
design -import gate -as gate
|
||||
miter -equiv -flatten -make_assert -make_outputs gold gate miter
|
||||
sat -verify -prove-asserts -show-ports miter
|
||||
Loading…
Reference in New Issue