aboutsummaryrefslogtreecommitdiffstats
path: root/passes
diff options
context:
space:
mode:
Diffstat (limited to 'passes')
-rw-r--r--passes/cmds/bugpoint.cc6
-rw-r--r--passes/cmds/rename.cc4
-rw-r--r--passes/cmds/show.cc6
-rw-r--r--passes/cmds/splice.cc2
-rw-r--r--passes/cmds/stat.cc2
-rw-r--r--passes/equiv/equiv_induct.cc4
-rw-r--r--passes/equiv/equiv_make.cc8
-rw-r--r--passes/equiv/equiv_purge.cc4
-rw-r--r--passes/hierarchy/hierarchy.cc14
-rw-r--r--passes/memory/memory_bram.cc211
-rw-r--r--passes/memory/memory_collect.cc232
-rw-r--r--passes/memory/memory_dff.cc144
-rw-r--r--passes/memory/memory_map.cc219
-rw-r--r--passes/memory/memory_nordff.cc72
-rw-r--r--passes/memory/memory_unpack.cc116
-rw-r--r--passes/opt/Makefile.inc2
-rw-r--r--passes/opt/opt.cc34
-rw-r--r--passes/opt/opt_clean.cc58
-rw-r--r--passes/opt/opt_dff.cc875
-rw-r--r--passes/opt/opt_expr.cc20
-rw-r--r--passes/opt/opt_mem.cc87
-rw-r--r--passes/opt/opt_rmdff.cc711
-rw-r--r--passes/opt/opt_share.cc361
-rw-r--r--passes/opt/pmux2shiftx.cc19
-rw-r--r--passes/opt/wreduce.cc44
-rw-r--r--passes/pmgen/Makefile.inc1
-rw-r--r--passes/pmgen/ice40_dsp.cc38
-rw-r--r--passes/pmgen/ice40_dsp.pmg295
-rw-r--r--passes/pmgen/peepopt.cc3
-rw-r--r--passes/pmgen/peepopt_dffmux.pmg171
-rw-r--r--passes/pmgen/peepopt_muldiv.pmg5
-rw-r--r--passes/pmgen/peepopt_shiftmul.pmg6
-rw-r--r--passes/pmgen/xilinx_dsp.cc159
-rw-r--r--passes/pmgen/xilinx_dsp.pmg333
-rw-r--r--passes/pmgen/xilinx_dsp48a.pmg322
-rw-r--r--passes/pmgen/xilinx_dsp_CREG.pmg122
-rw-r--r--passes/pmgen/xilinx_dsp_cascade.pmg125
-rw-r--r--passes/proc/proc.cc11
-rw-r--r--passes/proc/proc_dlatch.cc36
-rw-r--r--passes/sat/async2sync.cc302
-rw-r--r--passes/sat/clk2fflogic.cc392
-rw-r--r--passes/sat/mutate.cc6
-rw-r--r--passes/sat/sat.cc2
-rw-r--r--passes/sat/sim.cc214
-rw-r--r--passes/techmap/Makefile.inc3
-rw-r--r--passes/techmap/abc.cc44
-rw-r--r--passes/techmap/abc9.cc2
-rw-r--r--passes/techmap/abc9_exe.cc13
-rw-r--r--passes/techmap/dff2dffe.cc414
-rw-r--r--passes/techmap/dff2dffs.cc165
-rw-r--r--passes/techmap/dffinit.cc48
-rw-r--r--passes/techmap/dfflegalize.cc111
-rw-r--r--passes/techmap/dffunmap.cc107
-rw-r--r--passes/techmap/extract.cc10
-rw-r--r--passes/techmap/flatten.cc7
-rw-r--r--passes/techmap/shregmap.cc49
-rw-r--r--passes/techmap/techmap.cc122
-rw-r--r--passes/techmap/zinit.cc54
-rw-r--r--passes/tests/test_abcloop.cc2
-rw-r--r--passes/tests/test_cell.cc24
60 files changed, 2436 insertions, 4537 deletions
diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc
index 98d42aa83..81d7a34bb 100644
--- a/passes/cmds/bugpoint.cc
+++ b/passes/cmds/bugpoint.cc
@@ -18,10 +18,10 @@
*/
#include "kernel/yosys.h"
-#include "backends/ilang/ilang_backend.h"
+#include "backends/rtlil/rtlil_backend.h"
USING_YOSYS_NAMESPACE
-using namespace ILANG_BACKEND;
+using namespace RTLIL_BACKEND;
PRIVATE_NAMESPACE_BEGIN
struct BugpointPass : public Pass {
@@ -90,7 +90,7 @@ struct BugpointPass : public Pass {
design->sort();
std::ofstream f("bugpoint-case.il");
- ILANG_BACKEND::dump_design(f, design, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false);
+ RTLIL_BACKEND::dump_design(f, design, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false);
f.close();
string yosys_cmdline = stringf("%s -qq -L bugpoint-case.log -s %s bugpoint-case.il", yosys_cmd.c_str(), script.c_str());
diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc
index 6326b4b15..f8fe715c8 100644
--- a/passes/cmds/rename.cc
+++ b/passes/cmds/rename.cc
@@ -290,11 +290,11 @@ struct RenamePass : public Pass {
dict<RTLIL::Cell *, IdString> new_cell_names;
for (auto wire : module->selected_wires())
- if (wire->name[0] == '\\' && wire->port_id == 0)
+ if (wire->name.isPublic() && wire->port_id == 0)
new_wire_names[wire] = NEW_ID;
for (auto cell : module->selected_cells())
- if (cell->name[0] == '\\')
+ if (cell->name.isPublic())
new_cell_names[cell] = NEW_ID;
for (auto &it : new_wire_names)
diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc
index cbed08a3f..0c96f8c5d 100644
--- a/passes/cmds/show.cc
+++ b/passes/cmds/show.cc
@@ -368,7 +368,7 @@ struct ShowWorker
const char *shape = "diamond";
if (wire->port_input || wire->port_output)
shape = "octagon";
- if (wire->name[0] == '\\') {
+ if (wire->name.isPublic()) {
fprintf(f, "n%d [ shape=%s, label=\"%s\", %s, fontcolor=\"black\" ];\n",
id2num(wire->name), shape, findLabel(wire->name.str()),
nextColor(RTLIL::SigSpec(wire), "color=\"black\"").c_str());
@@ -605,7 +605,7 @@ struct ShowPass : public Pass {
log(" generate a .dot file, or other <format> strings such as 'svg' or 'ps'\n");
log(" to generate files in other formats (this calls the 'dot' command).\n");
log("\n");
- log(" -lib <verilog_or_ilang_file>\n");
+ log(" -lib <verilog_or_rtlil_file>\n");
log(" Use the specified library file for determining whether cell ports are\n");
log(" inputs or outputs. This option can be used multiple times to specify\n");
log(" more than one library.\n");
@@ -811,7 +811,7 @@ struct ShowPass : public Pass {
if (f.fail())
log_error("Can't open lib file `%s'.\n", filename.c_str());
RTLIL::Design *lib = new RTLIL::Design;
- Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.compare(filename.size()-3, std::string::npos, ".il") == 0 ? "ilang" : "verilog"));
+ Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.compare(filename.size()-3, std::string::npos, ".il") == 0 ? "rtlil" : "verilog"));
libs.push_back(lib);
}
diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc
index 20627d601..0f63b91c5 100644
--- a/passes/cmds/splice.cc
+++ b/passes/cmds/splice.cc
@@ -211,7 +211,7 @@ struct SpliceWorker
std::vector<Wire*> mod_wires = module->wires();
for (auto wire : mod_wires)
- if ((!no_outputs && wire->port_output) || (do_wires && wire->name[0] == '\\')) {
+ if ((!no_outputs && wire->port_output) || (do_wires && wire->name.isPublic())) {
if (!design->selected(module, wire))
continue;
RTLIL::SigSpec sig = sigmap(wire);
diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc
index ed51fdc24..0d84c73db 100644
--- a/passes/cmds/stat.cc
+++ b/passes/cmds/stat.cc
@@ -81,7 +81,7 @@ struct statdata_t
for (auto wire : mod->selected_wires())
{
- if (wire->name[0] == '\\') {
+ if (wire->name.isPublic()) {
num_pub_wires++;
num_pub_wire_bits += wire->width;
}
diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc
index 596c938fc..37aec50cd 100644
--- a/passes/equiv/equiv_induct.cc
+++ b/passes/equiv/equiv_induct.cc
@@ -65,8 +65,10 @@ struct EquivInductWorker
int ez_a = satgen.importSigBit(bit_a, step);
int ez_b = satgen.importSigBit(bit_b, step);
int cond = ez->IFF(ez_a, ez_b);
- if (satgen.model_undef)
+ if (satgen.model_undef) {
+ cond = ez->AND(cond, ez->NOT(satgen.importUndefSigBit(bit_b, step)));
cond = ez->OR(cond, satgen.importUndefSigBit(bit_a, step));
+ }
ez_equal_terms.push_back(cond);
}
}
diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc
index 51b4ad0f1..6923ae3d0 100644
--- a/passes/equiv/equiv_make.cc
+++ b/passes/equiv/equiv_make.cc
@@ -114,25 +114,25 @@ struct EquivMakeWorker
Module *gate_clone = gate_mod->clone();
for (auto it : gold_clone->wires().to_vector()) {
- if ((it->name[0] == '\\' || inames) && blacklist_names.count(it->name) == 0)
+ if ((it->name.isPublic() || inames) && blacklist_names.count(it->name) == 0)
wire_names.insert(it->name);
gold_clone->rename(it, it->name.str() + "_gold");
}
for (auto it : gold_clone->cells().to_vector()) {
- if ((it->name[0] == '\\' || inames) && blacklist_names.count(it->name) == 0)
+ if ((it->name.isPublic() || inames) && blacklist_names.count(it->name) == 0)
cell_names.insert(it->name);
gold_clone->rename(it, it->name.str() + "_gold");
}
for (auto it : gate_clone->wires().to_vector()) {
- if ((it->name[0] == '\\' || inames) && blacklist_names.count(it->name) == 0)
+ if ((it->name.isPublic() || inames) && blacklist_names.count(it->name) == 0)
wire_names.insert(it->name);
gate_clone->rename(it, it->name.str() + "_gate");
}
for (auto it : gate_clone->cells().to_vector()) {
- if ((it->name[0] == '\\' || inames) && blacklist_names.count(it->name) == 0)
+ if ((it->name.isPublic() || inames) && blacklist_names.count(it->name) == 0)
cell_names.insert(it->name);
gate_clone->rename(it, it->name.str() + "_gate");
}
diff --git a/passes/equiv/equiv_purge.cc b/passes/equiv/equiv_purge.cc
index d15c8d183..a43ecec5a 100644
--- a/passes/equiv/equiv_purge.cc
+++ b/passes/equiv/equiv_purge.cc
@@ -35,7 +35,7 @@ struct EquivPurgeWorker
{
if (sig.is_wire()) {
Wire *wire = sig.as_wire();
- if (wire->name[0] == '\\') {
+ if (wire->name.isPublic()) {
if (!wire->port_output) {
log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname));
wire->port_output = true;
@@ -62,7 +62,7 @@ struct EquivPurgeWorker
{
if (sig.is_wire()) {
Wire *wire = sig.as_wire();
- if (wire->name[0] == '\\') {
+ if (wire->name.isPublic()) {
if (!wire->port_output) {
log(" Module input: %s\n", log_signal(wire));
wire->port_input = true;
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
index a2a428d15..225e1feae 100644
--- a/passes/hierarchy/hierarchy.cc
+++ b/passes/hierarchy/hierarchy.cc
@@ -224,7 +224,7 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
{
{".v", "verilog"},
{".sv", "verilog -sv"},
- {".il", "ilang"}
+ {".il", "rtlil"}
};
for (auto &ext : extensions_list)
@@ -765,11 +765,13 @@ struct HierarchyPass : public Pass {
top_mod = design->module(top_name);
dict<RTLIL::IdString, RTLIL::Const> top_parameters;
- for (auto &para : parameters) {
- SigSpec sig_value;
- if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second))
- log_cmd_error("Can't decode value '%s'!\n", para.second.c_str());
- top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const();
+ if ((top_mod == nullptr && design->module(abstract_id)) || top_mod != nullptr) {
+ for (auto &para : parameters) {
+ SigSpec sig_value;
+ if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second))
+ log_cmd_error("Can't decode value '%s'!\n", para.second.c_str());
+ top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const();
+ }
}
if (top_mod == nullptr && design->module(abstract_id))
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc
index 3cb0728b7..c6948fdba 100644
--- a/passes/memory/memory_bram.cc
+++ b/passes/memory/memory_bram.cc
@@ -18,6 +18,7 @@
*/
#include "kernel/yosys.h"
+#include "kernel/mem.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -400,9 +401,11 @@ struct rules_t
}
};
-bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, const rules_t::match_t &match, dict<string, int> &match_properties, int mode)
+bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t &bram, const rules_t::match_t &match, dict<string, int> &match_properties, int mode)
{
- Module *module = cell->module;
+ // We will modify ports — make a copy of the structure.
+ Mem mem(orig_mem);
+ Module *module = mem.module;
auto portinfos = bram.make_portinfos();
int dup_count = 1;
@@ -437,46 +440,17 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant);
// bram.dump_config();
- int mem_size = cell->getParam(ID::SIZE).as_int();
- int mem_abits = cell->getParam(ID::ABITS).as_int();
- int mem_width = cell->getParam(ID::WIDTH).as_int();
- // int mem_offset = cell->getParam(ID::OFFSET).as_int();
-
- bool cell_init = !SigSpec(cell->getParam(ID::INIT)).is_fully_undef();
+ bool cell_init = !mem.inits.empty();
vector<Const> initdata;
if (cell_init) {
- Const initparam = cell->getParam(ID::INIT);
- initdata.reserve(mem_size);
- for (int i=0; i < mem_size; i++)
- initdata.push_back(initparam.extract(mem_width*i, mem_width, State::Sx));
+ Const initparam = mem.get_init_data();
+ initdata.reserve(mem.size);
+ for (int i=0; i < mem.size; i++)
+ initdata.push_back(initparam.extract(mem.width*i, mem.width, State::Sx));
}
- int wr_ports = cell->getParam(ID::WR_PORTS).as_int();
- auto wr_clken = SigSpec(cell->getParam(ID::WR_CLK_ENABLE));
- auto wr_clkpol = SigSpec(cell->getParam(ID::WR_CLK_POLARITY));
- wr_clken.extend_u0(wr_ports);
- wr_clkpol.extend_u0(wr_ports);
-
- SigSpec wr_en = cell->getPort(ID::WR_EN);
- SigSpec wr_clk = cell->getPort(ID::WR_CLK);
- SigSpec wr_data = cell->getPort(ID::WR_DATA);
- SigSpec wr_addr = cell->getPort(ID::WR_ADDR);
-
- int rd_ports = cell->getParam(ID::RD_PORTS).as_int();
- auto rd_clken = SigSpec(cell->getParam(ID::RD_CLK_ENABLE));
- auto rd_clkpol = SigSpec(cell->getParam(ID::RD_CLK_POLARITY));
- auto rd_transp = SigSpec(cell->getParam(ID::RD_TRANSPARENT));
- rd_clken.extend_u0(rd_ports);
- rd_clkpol.extend_u0(rd_ports);
- rd_transp.extend_u0(rd_ports);
-
- SigSpec rd_en = cell->getPort(ID::RD_EN);
- SigSpec rd_clk = cell->getPort(ID::RD_CLK);
- SigSpec rd_data = cell->getPort(ID::RD_DATA);
- SigSpec rd_addr = cell->getPort(ID::RD_ADDR);
-
- if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && wr_ports > 0)
+ if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && !mem.wr_ports.empty())
{
int bucket_size = bram.dbits / portinfos.at(match.shuffle_enable - 'A').enable;
log(" Shuffle bit order to accommodate enable buckets of size %d..\n", bucket_size);
@@ -487,23 +461,23 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
std::vector<SigSpec> old_wr_data;
std::vector<SigSpec> old_rd_data;
- for (int i = 0; i < wr_ports; i++) {
- old_wr_en.push_back(wr_en.extract(i*mem_width, mem_width));
- old_wr_data.push_back(wr_data.extract(i*mem_width, mem_width));
+ for (auto &port : mem.wr_ports) {
+ old_wr_en.push_back(port.en);
+ old_wr_data.push_back(port.data);
}
- for (int i = 0; i < rd_ports; i++)
- old_rd_data.push_back(rd_data.extract(i*mem_width, mem_width));
+ for (auto &port : mem.rd_ports)
+ old_rd_data.push_back(port.data);
// analyze enable structure
std::vector<SigSpec> en_order;
dict<SigSpec, vector<int>> bits_wr_en;
- for (int i = 0; i < mem_width; i++) {
+ for (int i = 0; i < mem.width; i++) {
SigSpec sig;
- for (int j = 0; j < wr_ports; j++)
- sig.append(old_wr_en[j][i]);
+ for (auto &port : mem.wr_ports)
+ sig.append(port.en[i]);
if (bits_wr_en.count(sig) == 0)
en_order.push_back(sig);
bits_wr_en[sig].push_back(i);
@@ -518,7 +492,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
std::vector<int> shuffle_map;
if (cell_init)
- new_initdata.resize(mem_size);
+ new_initdata.resize(mem.size);
for (auto &it : en_order)
{
@@ -528,29 +502,29 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
SigBit fillbit;
for (int i = 0; i < GetSize(bits); i++) {
- for (int j = 0; j < wr_ports; j++) {
+ for (int j = 0; j < GetSize(mem.wr_ports); j++) {
new_wr_en[j].append(old_wr_en[j][bits[i]]);
new_wr_data[j].append(old_wr_data[j][bits[i]]);
fillbit = old_wr_en[j][bits[i]];
}
- for (int j = 0; j < rd_ports; j++)
+ for (int j = 0; j < GetSize(mem.rd_ports); j++)
new_rd_data[j].append(old_rd_data[j][bits[i]]);
if (cell_init) {
- for (int j = 0; j < mem_size; j++)
+ for (int j = 0; j < mem.size; j++)
new_initdata[j].push_back(initdata[j][bits[i]]);
}
shuffle_map.push_back(bits[i]);
}
for (int i = 0; i < fillbits; i++) {
- for (int j = 0; j < wr_ports; j++) {
+ for (int j = 0; j < GetSize(mem.wr_ports); j++) {
new_wr_en[j].append(fillbit);
new_wr_data[j].append(State::S0);
}
- for (int j = 0; j < rd_ports; j++)
+ for (int j = 0; j < GetSize(mem.rd_ports); j++)
new_rd_data[j].append(State::Sx);
if (cell_init) {
- for (int j = 0; j < mem_size; j++)
+ for (int j = 0; j < mem.size; j++)
new_initdata[j].push_back(State::Sx);
}
shuffle_map.push_back(-1);
@@ -564,40 +538,38 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
// update mem_*, wr_*, and rd_* variables
- mem_width = GetSize(new_wr_en.front());
- wr_en = SigSpec(0, wr_ports * mem_width);
- wr_data = SigSpec(0, wr_ports * mem_width);
- rd_data = SigSpec(0, rd_ports * mem_width);
+ mem.width = GetSize(new_wr_en.front());
- for (int i = 0; i < wr_ports; i++) {
- wr_en.replace(i*mem_width, new_wr_en[i]);
- wr_data.replace(i*mem_width, new_wr_data[i]);
+ for (int i = 0; i < GetSize(mem.wr_ports); i++) {
+ auto &port = mem.wr_ports[i];
+ port.en = new_wr_en[i];
+ port.data = new_wr_data[i];
}
- for (int i = 0; i < rd_ports; i++)
- rd_data.replace(i*mem_width, new_rd_data[i]);
+ for (int i = 0; i < GetSize(mem.rd_ports); i++) {
+ auto &port = mem.rd_ports[i];
+ port.data = new_rd_data[i];
+ }
if (cell_init) {
- for (int i = 0; i < mem_size; i++)
+ for (int i = 0; i < mem.size; i++)
initdata[i] = Const(new_initdata[i]);
}
}
// assign write ports
pair<SigBit, bool> wr_clkdom;
- for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < wr_ports; cell_port_i++)
+ for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < GetSize(mem.wr_ports); cell_port_i++)
{
- bool clken = wr_clken[cell_port_i] == State::S1;
- bool clkpol = wr_clkpol[cell_port_i] == State::S1;
- SigBit clksig = wr_clk[cell_port_i];
+ auto &port = mem.wr_ports[cell_port_i];
- pair<SigBit, bool> clkdom(clksig, clkpol);
- if (!clken)
+ pair<SigBit, bool> clkdom(port.clk, port.clk_polarity);
+ if (!port.clk_enable)
clkdom = pair<SigBit, bool>(State::S1, false);
wr_clkdom = clkdom;
log(" Write port #%d is in clock domain %s%s.\n",
cell_port_i, clkdom.second ? "" : "!",
- clken ? log_signal(clkdom.first) : "~async~");
+ port.clk_enable ? log_signal(clkdom.first) : "~async~");
for (; bram_port_i < GetSize(portinfos); bram_port_i++)
{
@@ -609,7 +581,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
skip_bram_wport:
continue;
- if (clken) {
+ if (port.clk_enable) {
if (pi.clocks == 0) {
log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1);
goto skip_bram_wport;
@@ -618,7 +590,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
log(" Bram port %c%d is in a different clock domain.\n", pi.group + 'A', pi.index + 1);
goto skip_bram_wport;
}
- if (clock_polarities.count(pi.clkpol) && clock_polarities.at(pi.clkpol) != clkpol) {
+ if (clock_polarities.count(pi.clkpol) && clock_polarities.at(pi.clkpol) != port.clk_polarity) {
log(" Bram port %c%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1);
goto skip_bram_wport;
}
@@ -631,12 +603,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
SigSpec sig_en;
SigBit last_en_bit = State::S1;
- for (int i = 0; i < mem_width; i++) {
+ for (int i = 0; i < mem.width; i++) {
if (pi.enable && i % (bram.dbits / pi.enable) == 0) {
- last_en_bit = wr_en[i + cell_port_i*mem_width];
+ last_en_bit = port.en[i];
sig_en.append(last_en_bit);
}
- if (last_en_bit != wr_en[i + cell_port_i*mem_width]) {
+ if (last_en_bit != port.en[i]) {
log(" Bram port %c%d has incompatible enable structure.\n", pi.group + 'A', pi.index + 1);
goto skip_bram_wport;
}
@@ -645,7 +617,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
log(" Mapped to bram port %c%d.\n", pi.group + 'A', pi.index + 1);
pi.mapped_port = cell_port_i;
- if (clken) {
+ if (port.clk_enable) {
clock_domains[pi.clocks] = clkdom;
clock_polarities[pi.clkpol] = clkdom.second;
pi.sig_clock = clkdom.first;
@@ -653,8 +625,8 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
}
pi.sig_en = sig_en;
- pi.sig_addr = wr_addr.extract(cell_port_i*mem_abits, mem_abits);
- pi.sig_data = wr_data.extract(cell_port_i*mem_width, mem_width);
+ pi.sig_addr = port.addr;
+ pi.sig_data = port.data;
bram_port_i++;
goto mapped_wr_port;
@@ -710,23 +682,21 @@ grow_read_ports:;
// assign read ports
- for (int cell_port_i = 0; cell_port_i < rd_ports; cell_port_i++)
+ for (int cell_port_i = 0; cell_port_i < GetSize(mem.rd_ports); cell_port_i++)
{
- bool clken = rd_clken[cell_port_i] == State::S1;
- bool clkpol = rd_clkpol[cell_port_i] == State::S1;
- bool transp = rd_transp[cell_port_i] == State::S1;
- SigBit clksig = rd_clk[cell_port_i];
+ auto &port = mem.rd_ports[cell_port_i];
+ bool transp = port.transparent;
- if (wr_ports == 0)
+ if (mem.wr_ports.empty())
transp = false;
- pair<SigBit, bool> clkdom(clksig, clkpol);
- if (!clken)
+ pair<SigBit, bool> clkdom(port.clk, port.clk_polarity);
+ if (!port.clk_enable)
clkdom = pair<SigBit, bool>(State::S1, false);
log(" Read port #%d is in clock domain %s%s.\n",
cell_port_i, clkdom.second ? "" : "!",
- clken ? log_signal(clkdom.first) : "~async~");
+ port.clk_enable ? log_signal(clkdom.first) : "~async~");
for (int bram_port_i = 0; bram_port_i < GetSize(portinfos); bram_port_i++)
{
@@ -736,7 +706,7 @@ grow_read_ports:;
skip_bram_rport:
continue;
- if (clken) {
+ if (port.clk_enable) {
if (pi.clocks == 0) {
if (match.make_outreg) {
pi.make_outreg = true;
@@ -749,20 +719,20 @@ grow_read_ports:;
log(" Bram port %c%d.%d is in a different clock domain.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
- if (clock_polarities.count(pi.clkpol) && clock_polarities.at(pi.clkpol) != clkpol) {
+ if (clock_polarities.count(pi.clkpol) && clock_polarities.at(pi.clkpol) != port.clk_polarity) {
log(" Bram port %c%d.%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
- if (rd_en[cell_port_i] != State::S1 && pi.enable == 0) {
+ if (port.en != State::S1 && pi.enable == 0) {
log(" Bram port %c%d.%d has no read enable input.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
skip_bram_rport_clkcheck:
if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) {
- if (match.make_transp && wr_ports <= 1) {
+ if (match.make_transp && GetSize(mem.wr_ports) <= 1) {
pi.make_transp = true;
if (pi.clocks != 0) {
- if (wr_ports == 1 && wr_clkdom != clkdom) {
+ if (GetSize(mem.wr_ports) == 1 && wr_clkdom != clkdom) {
log(" Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
@@ -783,18 +753,18 @@ grow_read_ports:;
log(" Mapped to bram port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
pi.mapped_port = cell_port_i;
- if (clken) {
+ if (port.clk_enable) {
clock_domains[pi.clocks] = clkdom;
clock_polarities[pi.clkpol] = clkdom.second;
if (!pi.make_transp)
read_transp[pi.transp] = transp;
pi.sig_clock = clkdom.first;
- pi.sig_en = rd_en[cell_port_i];
+ pi.sig_en = port.en;
pi.effective_clkpol = clkdom.second;
}
- pi.sig_addr = rd_addr.extract(cell_port_i*mem_abits, mem_abits);
- pi.sig_data = rd_data.extract(cell_port_i*mem_width, mem_width);
+ pi.sig_addr = port.addr;
+ pi.sig_data = port.data;
if (grow_read_ports_cursor < cell_port_i) {
grow_read_ports_cursor = cell_port_i;
@@ -820,11 +790,11 @@ grow_read_ports:;
match_properties["dups"] = dup_count;
match_properties["waste"] = match_properties["dups"] * match_properties["bwaste"];
- int cells = ((mem_width + bram.dbits - 1) / bram.dbits) * ((mem_size + (1 << bram.abits) - 1) / (1 << bram.abits));
+ int cells = ((mem.width + bram.dbits - 1) / bram.dbits) * ((mem.size + (1 << bram.abits) - 1) / (1 << bram.abits));
match_properties["efficiency"] = (100 * match_properties["bits"]) / (dup_count * cells * bram.dbits * (1 << bram.abits));
- match_properties["dcells"] = ((mem_width + bram.dbits - 1) / bram.dbits);
- match_properties["acells"] = ((mem_size + (1 << bram.abits) - 1) / (1 << bram.abits));
+ match_properties["dcells"] = ((mem.width + bram.dbits - 1) / bram.dbits);
+ match_properties["acells"] = ((mem.size + (1 << bram.abits) - 1) / (1 << bram.abits));
match_properties["cells"] = match_properties["dcells"] * match_properties["acells"] * match_properties["dups"];
log(" Updated properties: dups=%d waste=%d efficiency=%d\n",
@@ -857,8 +827,8 @@ grow_read_ports:;
bool exists = std::get<0>(term);
IdString key = std::get<1>(term);
const Const &value = std::get<2>(term);
- auto it = cell->attributes.find(key);
- if (it == cell->attributes.end()) {
+ auto it = mem.attributes.find(key);
+ if (it == mem.attributes.end()) {
if (exists)
continue;
found = true;
@@ -902,7 +872,7 @@ grow_read_ports:;
dict<SigSpec, pair<SigSpec, SigSpec>> dout_cache;
- for (int grid_d = 0; grid_d*bram.dbits < mem_width; grid_d++)
+ for (int grid_d = 0; grid_d*bram.dbits < mem.width; grid_d++)
{
SigSpec mktr_wraddr, mktr_wrdata, mktr_wrdata_q;
vector<SigSpec> mktr_wren;
@@ -912,14 +882,14 @@ grow_read_ports:;
mktr_wrdata = module->addWire(NEW_ID, bram.dbits);
mktr_wrdata_q = module->addWire(NEW_ID, bram.dbits);
module->addDff(NEW_ID, make_transp_clk.first, mktr_wrdata, mktr_wrdata_q, make_transp_clk.second);
- for (int grid_a = 0; grid_a*(1 << bram.abits) < mem_size; grid_a++)
+ for (int grid_a = 0; grid_a*(1 << bram.abits) < mem.size; grid_a++)
mktr_wren.push_back(module->addWire(NEW_ID, make_transp_enbits));
}
- for (int grid_a = 0; grid_a*(1 << bram.abits) < mem_size; grid_a++)
+ for (int grid_a = 0; grid_a*(1 << bram.abits) < mem.size; grid_a++)
for (int dupidx = 0; dupidx < dup_count; dupidx++)
{
- Cell *c = module->addCell(module->uniquify(stringf("%s.%d.%d.%d", cell->name.c_str(), grid_d, grid_a, dupidx)), bram.name);
+ Cell *c = module->addCell(module->uniquify(stringf("%s.%d.%d.%d", mem.memid.c_str(), grid_d, grid_a, dupidx)), bram.name);
log(" Creating %s cell at grid position <%d %d %d>: %s\n", log_id(bram.name), grid_d, grid_a, dupidx, log_id(c));
for (auto &vp : variant_params)
@@ -1063,22 +1033,22 @@ grow_read_ports:;
}
}
- module->remove(cell);
+ mem.remove();
return true;
}
-void handle_cell(Cell *cell, const rules_t &rules)
+void handle_memory(Mem &mem, const rules_t &rules)
{
- log("Processing %s.%s:\n", log_id(cell->module), log_id(cell));
+ log("Processing %s.%s:\n", log_id(mem.module), log_id(mem.memid));
- bool cell_init = !SigSpec(cell->getParam(ID::INIT)).is_fully_undef();
+ bool cell_init = !mem.inits.empty();
dict<string, int> match_properties;
- match_properties["words"] = cell->getParam(ID::SIZE).as_int();
- match_properties["abits"] = cell->getParam(ID::ABITS).as_int();
- match_properties["dbits"] = cell->getParam(ID::WIDTH).as_int();
- match_properties["wports"] = cell->getParam(ID::WR_PORTS).as_int();
- match_properties["rports"] = cell->getParam(ID::RD_PORTS).as_int();
+ match_properties["words"] = mem.size;
+ match_properties["abits"] = ceil_log2(mem.size);
+ match_properties["dbits"] = mem.width;
+ match_properties["wports"] = GetSize(mem.wr_ports);
+ match_properties["rports"] = GetSize(mem.rd_ports);
match_properties["bits"] = match_properties["words"] * match_properties["dbits"];
match_properties["ports"] = match_properties["wports"] + match_properties["rports"];
@@ -1181,8 +1151,8 @@ void handle_cell(Cell *cell, const rules_t &rules)
bool exists = std::get<0>(term);
IdString key = std::get<1>(term);
const Const &value = std::get<2>(term);
- auto it = cell->attributes.find(key);
- if (it == cell->attributes.end()) {
+ auto it = mem.attributes.find(key);
+ if (it == mem.attributes.end()) {
if (exists)
continue;
found = true;
@@ -1219,7 +1189,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
if (or_next_if_better && i+1 == GetSize(rules.matches) && vi+1 == GetSize(rules.brams.at(match.name)))
log_error("Found 'or_next_if_better' in last match rule.\n");
- if (!replace_cell(cell, rules, bram, match, match_properties, 1)) {
+ if (!replace_memory(mem, rules, bram, match, match_properties, 1)) {
log(" Mapping to bram type %s failed.\n", log_id(match.name));
failed_brams.insert(pair<IdString, int>(bram.name, bram.variant));
goto next_match_rule;
@@ -1246,12 +1216,12 @@ void handle_cell(Cell *cell, const rules_t &rules)
best_rule_cache.clear();
auto &best_bram = rules.brams.at(rules.matches.at(best_rule.first).name).at(best_rule.second);
- if (!replace_cell(cell, rules, best_bram, rules.matches.at(best_rule.first), match_properties, 2))
+ if (!replace_memory(mem, rules, best_bram, rules.matches.at(best_rule.first), match_properties, 2))
log_error("Mapping to bram type %s (variant %d) after pre-selection failed.\n", log_id(best_bram.name), best_bram.variant);
return;
}
- if (!replace_cell(cell, rules, bram, match, match_properties, 0)) {
+ if (!replace_memory(mem, rules, bram, match, match_properties, 0)) {
log(" Mapping to bram type %s failed.\n", log_id(match.name));
failed_brams.insert(pair<IdString, int>(bram.name, bram.variant));
goto next_match_rule;
@@ -1384,9 +1354,8 @@ struct MemoryBramPass : public Pass {
extra_args(args, argidx, design);
for (auto mod : design->selected_modules())
- for (auto cell : mod->selected_cells())
- if (cell->type == ID($mem))
- handle_cell(cell, rules);
+ for (auto &mem : Mem::get_selected_memories(mod))
+ handle_memory(mem, rules);
}
} MemoryBramPass;
diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc
index 7e82f47dc..ede6ca6a1 100644
--- a/passes/memory/memory_collect.cc
+++ b/passes/memory/memory_collect.cc
@@ -18,231 +18,11 @@
*/
#include "kernel/yosys.h"
-#include "kernel/sigtools.h"
+#include "kernel/mem.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-bool memcells_cmp(Cell *a, Cell *b)
-{
- if (a->type == ID($memrd) && b->type == ID($memrd))
- return a->name < b->name;
- if (a->type == ID($memrd) || b->type == ID($memrd))
- return (a->type == ID($memrd)) < (b->type == ID($memrd));
- return a->parameters.at(ID::PRIORITY).as_int() < b->parameters.at(ID::PRIORITY).as_int();
-}
-
-Cell *handle_memory(Module *module, RTLIL::Memory *memory)
-{
- log("Collecting $memrd, $memwr and $meminit for memory `%s' in module `%s':\n",
- memory->name.c_str(), module->name.c_str());
-
- Const init_data(State::Sx, memory->size * memory->width);
- SigMap sigmap(module);
-
- int wr_ports = 0;
- SigSpec sig_wr_clk;
- SigSpec sig_wr_clk_enable;
- SigSpec sig_wr_clk_polarity;
- SigSpec sig_wr_addr;
- SigSpec sig_wr_data;
- SigSpec sig_wr_en;
-
- int rd_ports = 0;
- SigSpec sig_rd_clk;
- SigSpec sig_rd_clk_enable;
- SigSpec sig_rd_clk_polarity;
- SigSpec sig_rd_transparent;
- SigSpec sig_rd_addr;
- SigSpec sig_rd_data;
- SigSpec sig_rd_en;
-
- int addr_bits = 0;
- std::vector<Cell*> memcells;
-
- for (auto cell : module->cells())
- if (cell->type.in(ID($memrd), ID($memwr), ID($meminit)) && memory->name == cell->parameters[ID::MEMID].decode_string()) {
- SigSpec addr = sigmap(cell->getPort(ID::ADDR));
- for (int i = 0; i < GetSize(addr); i++)
- if (addr[i] != State::S0)
- addr_bits = std::max(addr_bits, i+1);
- memcells.push_back(cell);
- }
-
- if (memory->start_offset == 0 && addr_bits < 30 && (1 << addr_bits) < memory->size)
- memory->size = 1 << addr_bits;
-
- if (memory->start_offset >= 0)
- addr_bits = std::min(addr_bits, ceil_log2(memory->size + memory->start_offset));
-
- addr_bits = std::max(addr_bits, 1);
-
- if (memcells.empty()) {
- log(" no cells found. removing memory.\n");
- return nullptr;
- }
-
- std::sort(memcells.begin(), memcells.end(), memcells_cmp);
-
- for (auto cell : memcells)
- {
- log(" %s (%s)\n", log_id(cell), log_id(cell->type));
-
- if (cell->type == ID($meminit))
- {
- SigSpec addr = sigmap(cell->getPort(ID::ADDR));
- SigSpec data = sigmap(cell->getPort(ID::DATA));
-
- if (!addr.is_fully_const())
- log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), log_id(cell));
- if (!data.is_fully_const())
- log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell));
-
- int offset = (addr.as_int() - memory->start_offset) * memory->width;
-
- if (offset < 0 || offset + GetSize(data) > GetSize(init_data))
- log_warning("Address %s in memory initialization %s is out-of-bounds.\n", log_signal(addr), log_id(cell));
-
- for (int i = 0; i < GetSize(data); i++)
- if (0 <= i+offset && i+offset < GetSize(init_data))
- init_data.bits[i+offset] = data[i].data;
-
- continue;
- }
-
- if (cell->type == ID($memwr))
- {
- SigSpec clk = sigmap(cell->getPort(ID::CLK));
- SigSpec clk_enable = SigSpec(cell->parameters[ID::CLK_ENABLE]);
- SigSpec clk_polarity = SigSpec(cell->parameters[ID::CLK_POLARITY]);
- SigSpec addr = sigmap(cell->getPort(ID::ADDR));
- SigSpec data = sigmap(cell->getPort(ID::DATA));
- SigSpec en = sigmap(cell->getPort(ID::EN));
-
- if (!en.is_fully_zero())
- {
- clk.extend_u0(1, false);
- clk_enable.extend_u0(1, false);
- clk_polarity.extend_u0(1, false);
- addr.extend_u0(addr_bits, false);
- data.extend_u0(memory->width, false);
- en.extend_u0(memory->width, false);
-
- sig_wr_clk.append(clk);
- sig_wr_clk_enable.append(clk_enable);
- sig_wr_clk_polarity.append(clk_polarity);
- sig_wr_addr.append(addr);
- sig_wr_data.append(data);
- sig_wr_en.append(en);
-
- wr_ports++;
- }
- continue;
- }
-
- if (cell->type == ID($memrd))
- {
- SigSpec clk = sigmap(cell->getPort(ID::CLK));
- SigSpec clk_enable = SigSpec(cell->parameters[ID::CLK_ENABLE]);
- SigSpec clk_polarity = SigSpec(cell->parameters[ID::CLK_POLARITY]);
- SigSpec transparent = SigSpec(cell->parameters[ID::TRANSPARENT]);
- SigSpec addr = sigmap(cell->getPort(ID::ADDR));
- SigSpec data = sigmap(cell->getPort(ID::DATA));
- SigSpec en = sigmap(cell->getPort(ID::EN));
-
- if (!en.is_fully_zero())
- {
- clk.extend_u0(1, false);
- clk_enable.extend_u0(1, false);
- clk_polarity.extend_u0(1, false);
- transparent.extend_u0(1, false);
- addr.extend_u0(addr_bits, false);
- data.extend_u0(memory->width, false);
-
- sig_rd_clk.append(clk);
- sig_rd_clk_enable.append(clk_enable);
- sig_rd_clk_polarity.append(clk_polarity);
- sig_rd_transparent.append(transparent);
- sig_rd_addr.append(addr);
- sig_rd_data.append(data);
- sig_rd_en.append(en);
-
- rd_ports++;
- }
- continue;
- }
- }
-
- std::stringstream sstr;
- sstr << "$mem$" << memory->name.str() << "$" << (autoidx++);
-
- Cell *mem = module->addCell(sstr.str(), ID($mem));
- mem->parameters[ID::MEMID] = Const(memory->name.str());
- mem->parameters[ID::WIDTH] = Const(memory->width);
- mem->parameters[ID::OFFSET] = Const(memory->start_offset);
- mem->parameters[ID::SIZE] = Const(memory->size);
- mem->parameters[ID::ABITS] = Const(addr_bits);
- mem->parameters[ID::INIT] = init_data;
-
- log_assert(sig_wr_clk.size() == wr_ports);
- log_assert(sig_wr_clk_enable.size() == wr_ports && sig_wr_clk_enable.is_fully_const());
- log_assert(sig_wr_clk_polarity.size() == wr_ports && sig_wr_clk_polarity.is_fully_const());
- log_assert(sig_wr_addr.size() == wr_ports * addr_bits);
- log_assert(sig_wr_data.size() == wr_ports * memory->width);
- log_assert(sig_wr_en.size() == wr_ports * memory->width);
-
- mem->parameters[ID::WR_PORTS] = Const(wr_ports);
- mem->parameters[ID::WR_CLK_ENABLE] = wr_ports ? sig_wr_clk_enable.as_const() : State::S0;
- mem->parameters[ID::WR_CLK_POLARITY] = wr_ports ? sig_wr_clk_polarity.as_const() : State::S0;
-
- mem->setPort(ID::WR_CLK, sig_wr_clk);
- mem->setPort(ID::WR_ADDR, sig_wr_addr);
- mem->setPort(ID::WR_DATA, sig_wr_data);
- mem->setPort(ID::WR_EN, sig_wr_en);
-
- log_assert(sig_rd_clk.size() == rd_ports);
- log_assert(sig_rd_clk_enable.size() == rd_ports && sig_rd_clk_enable.is_fully_const());
- log_assert(sig_rd_clk_polarity.size() == rd_ports && sig_rd_clk_polarity.is_fully_const());
- log_assert(sig_rd_addr.size() == rd_ports * addr_bits);
- log_assert(sig_rd_data.size() == rd_ports * memory->width);
-
- mem->parameters[ID::RD_PORTS] = Const(rd_ports);
- mem->parameters[ID::RD_CLK_ENABLE] = rd_ports ? sig_rd_clk_enable.as_const() : State::S0;
- mem->parameters[ID::RD_CLK_POLARITY] = rd_ports ? sig_rd_clk_polarity.as_const() : State::S0;
- mem->parameters[ID::RD_TRANSPARENT] = rd_ports ? sig_rd_transparent.as_const() : State::S0;
-
- mem->setPort(ID::RD_CLK, sig_rd_clk);
- mem->setPort(ID::RD_ADDR, sig_rd_addr);
- mem->setPort(ID::RD_DATA, sig_rd_data);
- mem->setPort(ID::RD_EN, sig_rd_en);
-
- // Copy attributes from RTLIL memory to $mem
- for (auto attr : memory->attributes)
- mem->attributes[attr.first] = attr.second;
-
- for (auto c : memcells)
- module->remove(c);
-
- return mem;
-}
-
-static void handle_module(Design *design, Module *module)
-{
- std::vector<pair<Cell*, IdString>> finqueue;
-
- for (auto &mem_it : module->memories)
- if (design->selected(module, mem_it.second)) {
- Cell *c = handle_memory(module, mem_it.second);
- finqueue.push_back(pair<Cell*, IdString>(c, mem_it.first));
- }
- for (auto &it : finqueue) {
- delete module->memories.at(it.second);
- module->memories.erase(it.second);
- if (it.first)
- module->rename(it.first, it.second);
- }
-}
-
struct MemoryCollectPass : public Pass {
MemoryCollectPass() : Pass("memory_collect", "creating multi-port memory cells") { }
void help() override
@@ -258,8 +38,14 @@ struct MemoryCollectPass : public Pass {
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
log_header(design, "Executing MEMORY_COLLECT pass (generating $mem cells).\n");
extra_args(args, 1, design);
- for (auto module : design->selected_modules())
- handle_module(design, module);
+ for (auto module : design->selected_modules()) {
+ for (auto &mem : Mem::get_selected_memories(module)) {
+ if (!mem.packed) {
+ mem.packed = true;
+ mem.emit();
+ }
+ }
+ }
}
} MemoryCollectPass;
diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc
index 947cf7b35..4adcb462e 100644
--- a/passes/memory/memory_dff.cc
+++ b/passes/memory/memory_dff.cc
@@ -20,6 +20,7 @@
#include <algorithm>
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -34,66 +35,158 @@ struct MemoryDffWorker
dict<SigBit, int> sigbit_users_count;
dict<SigSpec, Cell*> mux_cells_a, mux_cells_b;
pool<Cell*> forward_merged_dffs, candidate_dffs;
- pool<SigBit> init_bits;
+ FfInitVals initvals;
MemoryDffWorker(Module *module) : module(module), sigmap(module)
{
- for (auto wire : module->wires()) {
- if (wire->attributes.count(ID::init) == 0)
- continue;
- SigSpec sig = sigmap(wire);
- Const initval = wire->attributes.at(ID::init);
- for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++)
- if (initval[i] == State::S0 || initval[i] == State::S1)
- init_bits.insert(sig[i]);
- }
+ initvals.set(&sigmap, module);
}
- bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false)
+ bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity)
{
sigmap.apply(sig);
+ dict<SigBit, SigBit> cache;
+
for (auto &bit : sig)
{
+ if (cache.count(bit)) {
+ bit = cache[bit];
+ continue;
+ }
+
if (bit.wire == NULL)
continue;
- if (!after && init_bits.count(sigmap(bit)))
+ if (initvals(bit) != State::Sx)
return false;
for (auto cell : dff_cells)
{
- if (after && forward_merged_dffs.count(cell))
+ SigSpec this_clk = cell->getPort(ID::CLK);
+ bool this_clk_polarity = cell->parameters[ID::CLK_POLARITY].as_bool();
+
+ if (invbits.count(this_clk)) {
+ this_clk = invbits.at(this_clk);
+ this_clk_polarity = !this_clk_polarity;
+ }
+
+ if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
+ if (this_clk != clk)
+ continue;
+ if (this_clk_polarity != clk_polarity)
+ continue;
+ }
+
+ RTLIL::SigSpec q_norm = cell->getPort(ID::Q);
+ sigmap.apply(q_norm);
+
+ RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(ID::D));
+ if (d.size() != 1)
+ continue;
+
+ if (cell->type == ID($sdffce)) {
+ SigSpec rval = cell->parameters[ID::SRST_VALUE];
+ SigSpec rbit = q_norm.extract(bit, &rval);
+ if (cell->parameters[ID::SRST_POLARITY].as_bool())
+ d = module->Mux(NEW_ID, d, rbit, cell->getPort(ID::SRST));
+ else
+ d = module->Mux(NEW_ID, rbit, d, cell->getPort(ID::SRST));
+ }
+
+ if (cell->type.in(ID($dffe), ID($sdffe), ID($sdffce))) {
+ if (cell->parameters[ID::EN_POLARITY].as_bool())
+ d = module->Mux(NEW_ID, bit, d, cell->getPort(ID::EN));
+ else
+ d = module->Mux(NEW_ID, d, bit, cell->getPort(ID::EN));
+ }
+
+ if (cell->type.in(ID($sdff), ID($sdffe))) {
+ SigSpec rval = cell->parameters[ID::SRST_VALUE];
+ SigSpec rbit = q_norm.extract(bit, &rval);
+ if (cell->parameters[ID::SRST_POLARITY].as_bool())
+ d = module->Mux(NEW_ID, d, rbit, cell->getPort(ID::SRST));
+ else
+ d = module->Mux(NEW_ID, rbit, d, cell->getPort(ID::SRST));
+ }
+
+ cache[bit] = d;
+ bit = d;
+ clk = this_clk;
+ clk_polarity = this_clk_polarity;
+ candidate_dffs.insert(cell);
+ goto replaced_this_bit;
+ }
+
+ return false;
+ replaced_this_bit:;
+ }
+
+ return true;
+ }
+
+ bool find_sig_after_dffe(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, RTLIL::SigSpec &en, bool &en_polarity)
+ {
+ sigmap.apply(sig);
+
+ for (auto &bit : sig)
+ {
+ if (bit.wire == NULL)
+ continue;
+
+ for (auto cell : dff_cells)
+ {
+ if (forward_merged_dffs.count(cell))
+ continue;
+ if (!cell->type.in(ID($dff), ID($dffe)))
continue;
SigSpec this_clk = cell->getPort(ID::CLK);
bool this_clk_polarity = cell->parameters[ID::CLK_POLARITY].as_bool();
+ SigSpec this_en = State::S1;
+ bool this_en_polarity = true;
+
+ if (cell->type == ID($dffe)) {
+ this_en = cell->getPort(ID::EN);
+ this_en_polarity = cell->parameters[ID::EN_POLARITY].as_bool();
+ }
if (invbits.count(this_clk)) {
this_clk = invbits.at(this_clk);
this_clk_polarity = !this_clk_polarity;
}
+ if (invbits.count(this_en)) {
+ this_en = invbits.at(this_en);
+ this_en_polarity = !this_en_polarity;
+ }
+
if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
if (this_clk != clk)
continue;
if (this_clk_polarity != clk_polarity)
continue;
+ if (this_en != en)
+ continue;
+ if (this_en_polarity != en_polarity)
+ continue;
}
- RTLIL::SigSpec q_norm = cell->getPort(after ? ID::D : ID::Q);
+ RTLIL::SigSpec q_norm = cell->getPort(ID::D);
sigmap.apply(q_norm);
- RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(after ? ID::Q : ID::D));
+ RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(ID::Q));
if (d.size() != 1)
continue;
- if (after && init_bits.count(d))
+ if (initvals(d) != State::Sx)
return false;
bit = d;
clk = this_clk;
clk_polarity = this_clk_polarity;
+ en = this_en;
+ en_polarity = this_en_polarity;
candidate_dffs.insert(cell);
goto replaced_this_bit;
}
@@ -161,7 +254,7 @@ struct MemoryDffWorker
RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size());
for (auto cell : module->cells())
- if (cell->type == ID($dff)) {
+ if (cell->type.in(ID($dff), ID($dffe))) {
RTLIL::SigSpec new_q = cell->getPort(ID::Q);
new_q.replace(sig, new_sig);
cell->setPort(ID::Q, new_q);
@@ -173,8 +266,10 @@ struct MemoryDffWorker
log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
bool clk_polarity = 0;
+ bool en_polarity = 0;
RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx);
+ RTLIL::SigSpec en_data;
RTLIL::SigSpec sig_data = cell->getPort(ID::DATA);
for (auto bit : sigmap(sig_data))
@@ -198,9 +293,14 @@ struct MemoryDffWorker
if (sigbit_users_count[bit] > 1)
goto skip_ff_after_read_merging;
- if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) &&
+ if (find_sig_after_dffe(sig_data, clk_data, clk_polarity, en_data, en_polarity) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) &&
std::all_of(check_q.begin(), check_q.end(), [&](const SigSpec &cq) {return cq == sig_data; }))
{
+ if (en_data != State::S1 || !en_polarity) {
+ if (!en_polarity)
+ en_data = module->LogicNot(NEW_ID, en_data);
+ en.append(en_data);
+ }
disconnect_dff(sig_data);
cell->setPort(ID::CLK, clk_data);
cell->setPort(ID::EN, en.size() > 1 ? module->ReduceAnd(NEW_ID, en) : en);
@@ -214,11 +314,13 @@ struct MemoryDffWorker
}
else
{
- if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
+ if (find_sig_after_dffe(sig_data, clk_data, clk_polarity, en_data, en_polarity) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
{
+ if (!en_polarity)
+ en_data = module->LogicNot(NEW_ID, en_data);
disconnect_dff(sig_data);
cell->setPort(ID::CLK, clk_data);
- cell->setPort(ID::EN, State::S1);
+ cell->setPort(ID::EN, en_data);
cell->setPort(ID::DATA, sig_data);
cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(1);
cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity);
@@ -256,7 +358,7 @@ struct MemoryDffWorker
}
for (auto cell : module->cells()) {
- if (cell->type == ID($dff))
+ if (cell->type.in(ID($dff), ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)))
dff_cells.push_back(cell);
if (cell->type == ID($mux)) {
mux_cells_a[sigmap(cell->getPort(ID::A))] = cell;
diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc
index 80dd3957d..032b8fbbd 100644
--- a/passes/memory/memory_map.cc
+++ b/passes/memory/memory_map.cc
@@ -19,6 +19,7 @@
#include "kernel/register.h"
#include "kernel/log.h"
+#include "kernel/mem.h"
#include <sstream>
#include <set>
#include <stdlib.h>
@@ -97,35 +98,26 @@ struct MemoryMapWorker
return bit.wire;
}
- void handle_cell(RTLIL::Cell *cell)
+ void handle_memory(Mem &mem)
{
std::set<int> static_ports;
std::map<int, RTLIL::SigSpec> static_cells_map;
- int wr_ports = cell->parameters[ID::WR_PORTS].as_int();
- int rd_ports = cell->parameters[ID::RD_PORTS].as_int();
-
- int mem_size = cell->parameters[ID::SIZE].as_int();
- int mem_width = cell->parameters[ID::WIDTH].as_int();
- int mem_offset = cell->parameters[ID::OFFSET].as_int();
- int mem_abits = cell->parameters[ID::ABITS].as_int();
-
- SigSpec init_data = cell->getParam(ID::INIT);
- init_data.extend_u0(mem_size*mem_width, true);
+ SigSpec init_data = mem.get_init_data();
// delete unused memory cell
- if (wr_ports == 0 && rd_ports == 0) {
- module->remove(cell);
+ if (mem.rd_ports.empty()) {
+ mem.remove();
return;
}
- // check if attributes allow us to infer FFRAM for this cell
+ // check if attributes allow us to infer FFRAM for this memory
for (const auto &attr : attributes) {
- if (cell->attributes.count(attr.first)) {
- const auto &cell_attr = cell->attributes[attr.first];
+ if (mem.attributes.count(attr.first)) {
+ const auto &cell_attr = mem.attributes[attr.first];
if (attr.second.empty()) {
- log("Not mapping memory cell %s in module %s (attribute %s is set).\n",
- cell->name.c_str(), module->name.c_str(), attr.first.c_str());
+ log("Not mapping memory %s in module %s (attribute %s is set).\n",
+ mem.memid.c_str(), module->name.c_str(), attr.first.c_str());
return;
}
@@ -138,11 +130,11 @@ struct MemoryMapWorker
}
if (!found) {
if (cell_attr.flags & RTLIL::CONST_FLAG_STRING) {
- log("Not mapping memory cell %s in module %s (attribute %s is set to \"%s\").\n",
- cell->name.c_str(), module->name.c_str(), attr.first.c_str(), cell_attr.decode_string().c_str());
+ log("Not mapping memory %s in module %s (attribute %s is set to \"%s\").\n",
+ mem.memid.c_str(), module->name.c_str(), attr.first.c_str(), cell_attr.decode_string().c_str());
} else {
- log("Not mapping memory cell %s in module %s (attribute %s is set to %d).\n",
- cell->name.c_str(), module->name.c_str(), attr.first.c_str(), cell_attr.as_int());
+ log("Not mapping memory %s in module %s (attribute %s is set to %d).\n",
+ mem.memid.c_str(), module->name.c_str(), attr.first.c_str(), cell_attr.as_int());
}
return;
}
@@ -150,82 +142,75 @@ struct MemoryMapWorker
}
// all write ports must share the same clock
- RTLIL::SigSpec clocks = cell->getPort(ID::WR_CLK);
- RTLIL::Const clocks_pol = cell->parameters[ID::WR_CLK_POLARITY];
- RTLIL::Const clocks_en = cell->parameters[ID::WR_CLK_ENABLE];
- clocks_pol.bits.resize(wr_ports);
- clocks_en.bits.resize(wr_ports);
RTLIL::SigSpec refclock;
- RTLIL::State refclock_pol = RTLIL::State::Sx;
- for (int i = 0; i < clocks.size(); i++) {
- RTLIL::SigSpec wr_en = cell->getPort(ID::WR_EN).extract(i * mem_width, mem_width);
- if (wr_en.is_fully_const() && !wr_en.as_bool()) {
+ bool refclock_pol = false;
+ for (int i = 0; i < GetSize(mem.wr_ports); i++) {
+ auto &port = mem.wr_ports[i];
+ if (port.en.is_fully_const() && !port.en.as_bool()) {
static_ports.insert(i);
continue;
}
- if (clocks_en.bits[i] != RTLIL::State::S1) {
- RTLIL::SigSpec wr_addr = cell->getPort(ID::WR_ADDR).extract(i*mem_abits, mem_abits);
- RTLIL::SigSpec wr_data = cell->getPort(ID::WR_DATA).extract(i*mem_width, mem_width);
- if (wr_addr.is_fully_const()) {
- // FIXME: Actually we should check for wr_en.is_fully_const() also and
- // create a $adff cell with this ports wr_en input as reset pin when wr_en
+ if (!port.clk_enable) {
+ if (port.addr.is_fully_const()) {
+ // FIXME: Actually we should check for port.en.is_fully_const() also and
+ // create a $adff cell with this ports port.en input as reset pin when port.en
// is not a simple static 1.
- static_cells_map[wr_addr.as_int() - mem_offset] = wr_data;
+ static_cells_map[port.addr.as_int() - mem.start_offset] = port.data;
static_ports.insert(i);
continue;
}
- log("Not mapping memory cell %s in module %s (write port %d has no clock).\n",
- cell->name.c_str(), module->name.c_str(), i);
+ log("Not mapping memory %s in module %s (write port %d has no clock).\n",
+ mem.memid.c_str(), module->name.c_str(), i);
return;
}
if (refclock.size() == 0) {
- refclock = clocks.extract(i, 1);
- refclock_pol = clocks_pol.bits[i];
+ refclock = port.clk;
+ refclock_pol = port.clk_polarity;
}
- if (clocks.extract(i, 1) != refclock || clocks_pol.bits[i] != refclock_pol) {
- log("Not mapping memory cell %s in module %s (write clock %d is incompatible with other clocks).\n",
- cell->name.c_str(), module->name.c_str(), i);
+ if (port.clk != refclock || port.clk_polarity != refclock_pol) {
+ log("Not mapping memory %s in module %s (write clock %d is incompatible with other clocks).\n",
+ mem.memid.c_str(), module->name.c_str(), i);
return;
}
}
- log("Mapping memory cell %s in module %s:\n", cell->name.c_str(), module->name.c_str());
+ log("Mapping memory %s in module %s:\n", mem.memid.c_str(), module->name.c_str());
std::vector<RTLIL::SigSpec> data_reg_in;
std::vector<RTLIL::SigSpec> data_reg_out;
int count_static = 0;
- for (int i = 0; i < mem_size; i++)
+ for (int i = 0; i < mem.size; i++)
{
if (static_cells_map.count(i) > 0)
{
- data_reg_in.push_back(RTLIL::SigSpec(RTLIL::State::Sz, mem_width));
+ data_reg_in.push_back(RTLIL::SigSpec(RTLIL::State::Sz, mem.width));
data_reg_out.push_back(static_cells_map[i]);
count_static++;
}
else
{
- RTLIL::Cell *c = module->addCell(genid(cell->name, "", i), ID($dff));
- c->parameters[ID::WIDTH] = cell->parameters[ID::WIDTH];
- if (clocks_pol.bits.size() > 0) {
- c->parameters[ID::CLK_POLARITY] = RTLIL::Const(clocks_pol.bits[0]);
- c->setPort(ID::CLK, clocks.extract(0, 1));
+ RTLIL::Cell *c = module->addCell(genid(mem.memid, "", i), ID($dff));
+ c->parameters[ID::WIDTH] = mem.width;
+ if (GetSize(refclock) != 0) {
+ c->parameters[ID::CLK_POLARITY] = RTLIL::Const(refclock_pol);
+ c->setPort(ID::CLK, refclock);
} else {
c->parameters[ID::CLK_POLARITY] = RTLIL::Const(RTLIL::State::S1);
c->setPort(ID::CLK, RTLIL::SigSpec(RTLIL::State::S0));
}
- RTLIL::Wire *w_in = module->addWire(genid(cell->name, "", i, "$d"), mem_width);
+ RTLIL::Wire *w_in = module->addWire(genid(mem.memid, "", i, "$d"), mem.width);
data_reg_in.push_back(RTLIL::SigSpec(w_in));
c->setPort(ID::D, data_reg_in.back());
- std::string w_out_name = stringf("%s[%d]", cell->parameters[ID::MEMID].decode_string().c_str(), i);
+ std::string w_out_name = stringf("%s[%d]", mem.memid.c_str(), i);
if (module->wires_.count(w_out_name) > 0)
- w_out_name = genid(cell->name, "", i, "$q");
+ w_out_name = genid(mem.memid, "", i, "$q");
- RTLIL::Wire *w_out = module->addWire(w_out_name, mem_width);
- SigSpec w_init = init_data.extract(i*mem_width, mem_width);
+ RTLIL::Wire *w_out = module->addWire(w_out_name, mem.width);
+ SigSpec w_init = init_data.extract(i*mem.width, mem.width);
if (!w_init.is_fully_undef())
w_out->attributes[ID::init] = w_init.as_const();
@@ -235,76 +220,39 @@ struct MemoryMapWorker
}
}
- log(" created %d $dff cells and %d static cells of width %d.\n", mem_size-count_static, count_static, mem_width);
+ log(" created %d $dff cells and %d static cells of width %d.\n", mem.size-count_static, count_static, mem.width);
int count_dff = 0, count_mux = 0, count_wrmux = 0;
- for (int i = 0; i < cell->parameters[ID::RD_PORTS].as_int(); i++)
+ int abits = ceil_log2(mem.size);
+ for (int i = 0; i < GetSize(mem.rd_ports); i++)
{
- RTLIL::SigSpec rd_addr = cell->getPort(ID::RD_ADDR).extract(i*mem_abits, mem_abits);
+ auto &port = mem.rd_ports[i];
+ if (mem.extract_rdff(i))
+ count_dff++;
+ RTLIL::SigSpec rd_addr = port.addr;
+ rd_addr.extend_u0(abits, false);
- if (mem_offset)
- rd_addr = module->Sub(NEW_ID, rd_addr, SigSpec(mem_offset, GetSize(rd_addr)));
+ if (mem.start_offset)
+ rd_addr = module->Sub(NEW_ID, rd_addr, SigSpec(mem.start_offset, abits));
std::vector<RTLIL::SigSpec> rd_signals;
- rd_signals.push_back(cell->getPort(ID::RD_DATA).extract(i*mem_width, mem_width));
-
- if (cell->parameters[ID::RD_CLK_ENABLE].bits[i] == RTLIL::State::S1)
- {
- RTLIL::Cell *dff_cell = nullptr;
-
- if (cell->parameters[ID::RD_TRANSPARENT].bits[i] == RTLIL::State::S1)
- {
- dff_cell = module->addCell(genid(cell->name, "$rdreg", i), ID($dff));
- dff_cell->parameters[ID::WIDTH] = RTLIL::Const(mem_abits);
- dff_cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(cell->parameters[ID::RD_CLK_POLARITY].bits[i]);
- dff_cell->setPort(ID::CLK, cell->getPort(ID::RD_CLK).extract(i, 1));
- dff_cell->setPort(ID::D, rd_addr);
- count_dff++;
-
- RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$q"), mem_abits);
-
- dff_cell->setPort(ID::Q, RTLIL::SigSpec(w));
- rd_addr = RTLIL::SigSpec(w);
- }
- else
- {
- dff_cell = module->addCell(genid(cell->name, "$rdreg", i), ID($dff));
- dff_cell->parameters[ID::WIDTH] = cell->parameters[ID::WIDTH];
- dff_cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(cell->parameters[ID::RD_CLK_POLARITY].bits[i]);
- dff_cell->setPort(ID::CLK, cell->getPort(ID::RD_CLK).extract(i, 1));
- dff_cell->setPort(ID::Q, rd_signals.back());
- count_dff++;
-
- RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$d"), mem_width);
-
- rd_signals.clear();
- rd_signals.push_back(RTLIL::SigSpec(w));
- dff_cell->setPort(ID::D, rd_signals.back());
- }
-
- SigBit en_bit = cell->getPort(ID::RD_EN).extract(i);
- if (en_bit != State::S1) {
- SigSpec new_d = module->Mux(genid(cell->name, "$rdenmux", i),
- dff_cell->getPort(ID::Q), dff_cell->getPort(ID::D), en_bit);
- dff_cell->setPort(ID::D, new_d);
- }
- }
+ rd_signals.push_back(port.data);
- for (int j = 0; j < mem_abits; j++)
+ for (int j = 0; j < abits; j++)
{
std::vector<RTLIL::SigSpec> next_rd_signals;
for (size_t k = 0; k < rd_signals.size(); k++)
{
- RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdmux", i, "", j, "", k), ID($mux));
- c->parameters[ID::WIDTH] = cell->parameters[ID::WIDTH];
+ RTLIL::Cell *c = module->addCell(genid(mem.memid, "$rdmux", i, "", j, "", k), ID($mux));
+ c->parameters[ID::WIDTH] = mem.width;
c->setPort(ID::Y, rd_signals[k]);
- c->setPort(ID::S, rd_addr.extract(mem_abits-j-1, 1));
+ c->setPort(ID::S, rd_addr.extract(abits-j-1, 1));
count_mux++;
- c->setPort(ID::A, module->addWire(genid(cell->name, "$rdmux", i, "", j, "", k, "$a"), mem_width));
- c->setPort(ID::B, module->addWire(genid(cell->name, "$rdmux", i, "", j, "", k, "$b"), mem_width));
+ c->setPort(ID::A, module->addWire(genid(mem.memid, "$rdmux", i, "", j, "", k, "$a"), mem.width));
+ c->setPort(ID::B, module->addWire(genid(mem.memid, "$rdmux", i, "", j, "", k, "$b"), mem.width));
next_rd_signals.push_back(c->getPort(ID::A));
next_rd_signals.push_back(c->getPort(ID::B));
@@ -313,38 +261,37 @@ struct MemoryMapWorker
next_rd_signals.swap(rd_signals);
}
- for (int j = 0; j < mem_size; j++)
+ for (int j = 0; j < mem.size; j++)
module->connect(RTLIL::SigSig(rd_signals[j], data_reg_out[j]));
}
log(" read interface: %d $dff and %d $mux cells.\n", count_dff, count_mux);
- for (int i = 0; i < mem_size; i++)
+ for (int i = 0; i < mem.size; i++)
{
if (static_cells_map.count(i) > 0)
continue;
RTLIL::SigSpec sig = data_reg_out[i];
- for (int j = 0; j < cell->parameters[ID::WR_PORTS].as_int(); j++)
+ for (int j = 0; j < GetSize(mem.wr_ports); j++)
{
- RTLIL::SigSpec wr_addr = cell->getPort(ID::WR_ADDR).extract(j*mem_abits, mem_abits);
- RTLIL::SigSpec wr_data = cell->getPort(ID::WR_DATA).extract(j*mem_width, mem_width);
- RTLIL::SigSpec wr_en = cell->getPort(ID::WR_EN).extract(j*mem_width, mem_width);
+ auto &port = mem.wr_ports[j];
+ RTLIL::SigSpec wr_addr = port.addr;
- if (mem_offset)
- wr_addr = module->Sub(NEW_ID, wr_addr, SigSpec(mem_offset, GetSize(wr_addr)));
+ if (mem.start_offset)
+ wr_addr = module->Sub(NEW_ID, wr_addr, SigSpec(mem.start_offset, GetSize(wr_addr)));
- RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(i, mem_abits));
+ RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(i, GetSize(wr_addr)));
int wr_offset = 0;
- while (wr_offset < wr_en.size())
+ while (wr_offset < port.en.size())
{
int wr_width = 1;
- RTLIL::SigSpec wr_bit = wr_en.extract(wr_offset, 1);
+ RTLIL::SigSpec wr_bit = port.en.extract(wr_offset, 1);
- while (wr_offset + wr_width < wr_en.size()) {
- RTLIL::SigSpec next_wr_bit = wr_en.extract(wr_offset + wr_width, 1);
+ while (wr_offset + wr_width < port.en.size()) {
+ RTLIL::SigSpec next_wr_bit = port.en.extract(wr_offset + wr_width, 1);
if (next_wr_bit != wr_bit)
break;
wr_width++;
@@ -354,7 +301,7 @@ struct MemoryMapWorker
if (wr_bit != State::S1)
{
- RTLIL::Cell *c = module->addCell(genid(cell->name, "$wren", i, "", j, "", wr_offset), ID($and));
+ RTLIL::Cell *c = module->addCell(genid(mem.memid, "$wren", i, "", j, "", wr_offset), ID($and));
c->parameters[ID::A_SIGNED] = RTLIL::Const(0);
c->parameters[ID::B_SIGNED] = RTLIL::Const(0);
c->parameters[ID::A_WIDTH] = RTLIL::Const(1);
@@ -363,17 +310,17 @@ struct MemoryMapWorker
c->setPort(ID::A, w);
c->setPort(ID::B, wr_bit);
- w = module->addWire(genid(cell->name, "$wren", i, "", j, "", wr_offset, "$y"));
+ w = module->addWire(genid(mem.memid, "$wren", i, "", j, "", wr_offset, "$y"));
c->setPort(ID::Y, RTLIL::SigSpec(w));
}
- RTLIL::Cell *c = module->addCell(genid(cell->name, "$wrmux", i, "", j, "", wr_offset), ID($mux));
+ RTLIL::Cell *c = module->addCell(genid(mem.memid, "$wrmux", i, "", j, "", wr_offset), ID($mux));
c->parameters[ID::WIDTH] = wr_width;
c->setPort(ID::A, sig.extract(wr_offset, wr_width));
- c->setPort(ID::B, wr_data.extract(wr_offset, wr_width));
+ c->setPort(ID::B, port.data.extract(wr_offset, wr_width));
c->setPort(ID::S, RTLIL::SigSpec(w));
- w = module->addWire(genid(cell->name, "$wrmux", i, "", j, "", wr_offset, "$y"), wr_width);
+ w = module->addWire(genid(mem.memid, "$wrmux", i, "", j, "", wr_offset, "$y"), wr_width);
c->setPort(ID::Y, w);
sig.replace(wr_offset, w);
@@ -387,17 +334,13 @@ struct MemoryMapWorker
log(" write interface: %d write mux blocks.\n", count_wrmux);
- module->remove(cell);
+ mem.remove();
}
void run()
{
- std::vector<RTLIL::Cell*> cells;
- for (auto cell : module->selected_cells())
- if (cell->type == ID($mem))
- cells.push_back(cell);
- for (auto cell : cells)
- handle_cell(cell);
+ for (auto &mem : Mem::get_selected_memories(module))
+ handle_memory(mem);
}
};
@@ -430,7 +373,7 @@ struct MemoryMapPass : public Pass {
bool attr_icase = false;
dict<RTLIL::IdString, std::vector<RTLIL::Const>> attributes;
- log_header(design, "Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n");
+ log_header(design, "Executing MEMORY_MAP pass (converting memories to logic and flip-flops).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
diff --git a/passes/memory/memory_nordff.cc b/passes/memory/memory_nordff.cc
index 07bbd9fe8..a4fdcfc38 100644
--- a/passes/memory/memory_nordff.cc
+++ b/passes/memory/memory_nordff.cc
@@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/mem.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -37,7 +38,7 @@ struct MemoryNordffPass : public Pass {
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
- log_header(design, "Executing MEMORY_NORDFF pass (extracting $dff cells from $mem).\n");
+ log_header(design, "Executing MEMORY_NORDFF pass (extracting $dff cells from memories).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -50,70 +51,15 @@ struct MemoryNordffPass : public Pass {
extra_args(args, argidx, design);
for (auto module : design->selected_modules())
- for (auto cell : vector<Cell*>(module->selected_cells()))
+ for (auto &mem : Mem::get_selected_memories(module))
{
- if (cell->type != ID($mem))
- continue;
+ bool changed = false;
+ for (int i = 0; i < GetSize(mem.rd_ports); i++)
+ if (mem.extract_rdff(i))
+ changed = true;
- int rd_ports = cell->getParam(ID::RD_PORTS).as_int();
- int abits = cell->getParam(ID::ABITS).as_int();
- int width = cell->getParam(ID::WIDTH).as_int();
-
- SigSpec rd_addr = cell->getPort(ID::RD_ADDR);
- SigSpec rd_data = cell->getPort(ID::RD_DATA);
- SigSpec rd_clk = cell->getPort(ID::RD_CLK);
- SigSpec rd_en = cell->getPort(ID::RD_EN);
- Const rd_clk_enable = cell->getParam(ID::RD_CLK_ENABLE);
- Const rd_clk_polarity = cell->getParam(ID::RD_CLK_POLARITY);
-
- for (int i = 0; i < rd_ports; i++)
- {
- bool clk_enable = rd_clk_enable[i] == State::S1;
-
- if (clk_enable)
- {
- bool clk_polarity = cell->getParam(ID::RD_CLK_POLARITY)[i] == State::S1;
- bool transparent = cell->getParam(ID::RD_TRANSPARENT)[i] == State::S1;
-
- SigSpec clk = cell->getPort(ID::RD_CLK)[i] ;
- SigSpec en = cell->getPort(ID::RD_EN)[i];
- Cell *c;
-
- if (transparent)
- {
- SigSpec sig_q = module->addWire(NEW_ID, abits);
- SigSpec sig_d = rd_addr.extract(abits * i, abits);
- rd_addr.replace(abits * i, sig_q);
- if (en != State::S1)
- sig_d = module->Mux(NEW_ID, sig_q, sig_d, en);
- c = module->addDff(NEW_ID, clk, sig_d, sig_q, clk_polarity);
- }
- else
- {
- SigSpec sig_d = module->addWire(NEW_ID, width);
- SigSpec sig_q = rd_data.extract(width * i, width);
- rd_data.replace(width *i, sig_d);
- if (en != State::S1)
- sig_d = module->Mux(NEW_ID, sig_q, sig_d, en);
- c = module->addDff(NEW_ID, clk, sig_d, sig_q, clk_polarity);
- }
-
- log("Extracted %s FF from read port %d of %s.%s: %s\n", transparent ? "addr" : "data",
- i, log_id(module), log_id(cell), log_id(c));
- }
-
- rd_en[i] = State::S1;
- rd_clk[i] = State::S0;
- rd_clk_enable[i] = State::S0;
- rd_clk_polarity[i] = State::S1;
- }
-
- cell->setPort(ID::RD_ADDR, rd_addr);
- cell->setPort(ID::RD_DATA, rd_data);
- cell->setPort(ID::RD_CLK, rd_clk);
- cell->setPort(ID::RD_EN, rd_en);
- cell->setParam(ID::RD_CLK_ENABLE, rd_clk_enable);
- cell->setParam(ID::RD_CLK_POLARITY, rd_clk_polarity);
+ if (changed)
+ mem.emit();
}
}
} MemoryNordffPass;
diff --git a/passes/memory/memory_unpack.cc b/passes/memory/memory_unpack.cc
index d04d4ba7a..16b57d9c3 100644
--- a/passes/memory/memory_unpack.cc
+++ b/passes/memory/memory_unpack.cc
@@ -17,114 +17,12 @@
*
*/
-#include "kernel/register.h"
-#include "kernel/log.h"
-#include <sstream>
-#include <algorithm>
-#include <stdlib.h>
+#include "kernel/yosys.h"
+#include "kernel/mem.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory)
-{
- log("Creating $memrd and $memwr for memory `%s' in module `%s':\n",
- memory->name.c_str(), module->name.c_str());
-
- RTLIL::IdString mem_name = RTLIL::escape_id(memory->parameters.at(ID::MEMID).decode_string());
-
- while (module->memories.count(mem_name) != 0)
- mem_name = mem_name.str() + stringf("_%d", autoidx++);
-
- RTLIL::Memory *mem = new RTLIL::Memory;
- mem->name = mem_name;
- mem->width = memory->parameters.at(ID::WIDTH).as_int();
- mem->start_offset = memory->parameters.at(ID::OFFSET).as_int();
- mem->size = memory->parameters.at(ID::SIZE).as_int();
- module->memories[mem_name] = mem;
-
- int abits = memory->parameters.at(ID::ABITS).as_int();
- int num_rd_ports = memory->parameters.at(ID::RD_PORTS).as_int();
- int num_wr_ports = memory->parameters.at(ID::WR_PORTS).as_int();
-
- for (int i = 0; i < num_rd_ports; i++)
- {
- RTLIL::Cell *cell = module->addCell(NEW_ID, ID($memrd));
- cell->parameters[ID::MEMID] = mem_name.str();
- cell->parameters[ID::ABITS] = memory->parameters.at(ID::ABITS);
- cell->parameters[ID::WIDTH] = memory->parameters.at(ID::WIDTH);
- cell->parameters[ID::CLK_ENABLE] = RTLIL::SigSpec(memory->parameters.at(ID::RD_CLK_ENABLE)).extract(i, 1).as_const();
- cell->parameters[ID::CLK_POLARITY] = RTLIL::SigSpec(memory->parameters.at(ID::RD_CLK_POLARITY)).extract(i, 1).as_const();
- cell->parameters[ID::TRANSPARENT] = RTLIL::SigSpec(memory->parameters.at(ID::RD_TRANSPARENT)).extract(i, 1).as_const();
- cell->setPort(ID::CLK, memory->getPort(ID::RD_CLK).extract(i, 1));
- cell->setPort(ID::EN, memory->getPort(ID::RD_EN).extract(i, 1));
- cell->setPort(ID::ADDR, memory->getPort(ID::RD_ADDR).extract(i*abits, abits));
- cell->setPort(ID::DATA, memory->getPort(ID::RD_DATA).extract(i*mem->width, mem->width));
- }
-
- for (int i = 0; i < num_wr_ports; i++)
- {
- RTLIL::Cell *cell = module->addCell(NEW_ID, ID($memwr));
- cell->parameters[ID::MEMID] = mem_name.str();
- cell->parameters[ID::ABITS] = memory->parameters.at(ID::ABITS);
- cell->parameters[ID::WIDTH] = memory->parameters.at(ID::WIDTH);
- cell->parameters[ID::CLK_ENABLE] = RTLIL::SigSpec(memory->parameters.at(ID::WR_CLK_ENABLE)).extract(i, 1).as_const();
- cell->parameters[ID::CLK_POLARITY] = RTLIL::SigSpec(memory->parameters.at(ID::WR_CLK_POLARITY)).extract(i, 1).as_const();
- cell->parameters[ID::PRIORITY] = i;
- cell->setPort(ID::CLK, memory->getPort(ID::WR_CLK).extract(i, 1));
- cell->setPort(ID::EN, memory->getPort(ID::WR_EN).extract(i*mem->width, mem->width));
- cell->setPort(ID::ADDR, memory->getPort(ID::WR_ADDR).extract(i*abits, abits));
- cell->setPort(ID::DATA, memory->getPort(ID::WR_DATA).extract(i*mem->width, mem->width));
- }
-
- Const initval = memory->parameters.at(ID::INIT);
- RTLIL::Cell *last_init_cell = nullptr;
- SigSpec last_init_data;
- int last_init_addr=0;
-
- for (int i = 0; i < GetSize(initval) && i/mem->width < (1 << abits); i += mem->width) {
- Const val = initval.extract(i, mem->width, State::Sx);
- for (auto bit : val.bits)
- if (bit != State::Sx)
- goto found_non_undef_initval;
- continue;
- found_non_undef_initval:
- if (last_init_cell && last_init_addr+1 == i/mem->width) {
- last_init_cell->parameters[ID::WORDS] = last_init_cell->parameters[ID::WORDS].as_int() + 1;
- last_init_data.append(val);
- last_init_addr++;
- } else {
- if (last_init_cell)
- last_init_cell->setPort(ID::DATA, last_init_data);
- RTLIL::Cell *cell = module->addCell(NEW_ID, ID($meminit));
- cell->parameters[ID::MEMID] = mem_name.str();
- cell->parameters[ID::ABITS] = memory->parameters.at(ID::ABITS);
- cell->parameters[ID::WIDTH] = memory->parameters.at(ID::WIDTH);
- cell->parameters[ID::WORDS] = 1;
- cell->parameters[ID::PRIORITY] = i/mem->width;
- cell->setPort(ID::ADDR, SigSpec(i/mem->width, abits));
- last_init_cell = cell;
- last_init_addr = i/mem->width;
- last_init_data = val;
- }
- }
-
- if (last_init_cell)
- last_init_cell->setPort(ID::DATA, last_init_data);
-
- module->remove(memory);
-}
-
-void handle_module(RTLIL::Design *design, RTLIL::Module *module)
-{
- std::vector<RTLIL::IdString> memcells;
- for (auto cell : module->cells())
- if (cell->type == ID($mem) && design->selected(module, cell))
- memcells.push_back(cell->name);
- for (auto &it : memcells)
- handle_memory(module, module->cell(it));
-}
-
struct MemoryUnpackPass : public Pass {
MemoryUnpackPass() : Pass("memory_unpack", "unpack multi-port memory cells") { }
void help() override
@@ -140,8 +38,14 @@ struct MemoryUnpackPass : public Pass {
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
log_header(design, "Executing MEMORY_UNPACK pass (generating $memrd/$memwr cells form $mem cells).\n");
extra_args(args, 1, design);
- for (auto module : design->selected_modules())
- handle_module(design, module);
+ for (auto module : design->selected_modules()) {
+ for (auto &mem : Mem::get_selected_memories(module)) {
+ if (mem.packed) {
+ mem.packed = false;
+ mem.emit();
+ }
+ }
+ }
}
} MemoryUnpackPass;
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
index 3133927bb..4ae9b8895 100644
--- a/passes/opt/Makefile.inc
+++ b/passes/opt/Makefile.inc
@@ -4,7 +4,7 @@ OBJS += passes/opt/opt_merge.o
OBJS += passes/opt/opt_mem.o
OBJS += passes/opt/opt_muxtree.o
OBJS += passes/opt/opt_reduce.o
-OBJS += passes/opt/opt_rmdff.o
+OBJS += passes/opt/opt_dff.o
OBJS += passes/opt/opt_share.o
OBJS += passes/opt/opt_clean.o
OBJS += passes/opt/opt_expr.o
diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc
index 8be94e345..4b052d9a2 100644
--- a/passes/opt/opt.cc
+++ b/passes/opt/opt.cc
@@ -37,7 +37,7 @@ struct OptPass : public Pass {
log("a series of trivial optimizations and cleanups. This pass executes the other\n");
log("passes in the following order:\n");
log("\n");
- log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-noclkinv] [-fine] [-full] [-keepdc]\n");
log(" opt_merge [-share_all] -nomux\n");
log("\n");
log(" do\n");
@@ -45,19 +45,19 @@ struct OptPass : public Pass {
log(" opt_reduce [-fine] [-full]\n");
log(" opt_merge [-share_all]\n");
log(" opt_share (-full only)\n");
- log(" opt_rmdff [-keepdc] [-sat] (except when called with -noff)\n");
+ log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] (except when called with -noff)\n");
log(" opt_clean [-purge]\n");
- log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-noclkinv] [-fine] [-full] [-keepdc]\n");
log(" while <changed design>\n");
log("\n");
log("When called with -fast the following script is used instead:\n");
log("\n");
log(" do\n");
- log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-noclkinv] [-fine] [-full] [-keepdc]\n");
log(" opt_merge [-share_all]\n");
- log(" opt_rmdff [-keepdc] [-sat] (except when called with -noff)\n");
+ log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] (except when called with -noff)\n");
log(" opt_clean [-purge]\n");
- log(" while <changed design in opt_rmdff>\n");
+ log(" while <changed design in opt_dff>\n");
log("\n");
log("Note: Options in square brackets (such as [-keepdc]) are passed through to\n");
log("the opt_* commands when given to 'opt'.\n");
@@ -70,7 +70,7 @@ struct OptPass : public Pass {
std::string opt_expr_args;
std::string opt_reduce_args;
std::string opt_merge_args;
- std::string opt_rmdff_args;
+ std::string opt_dff_args;
bool opt_share = false;
bool fast_mode = false;
bool noff_mode = false;
@@ -96,8 +96,8 @@ struct OptPass : public Pass {
opt_expr_args += " -undriven";
continue;
}
- if (args[argidx] == "-clkinv") {
- opt_expr_args += " -clkinv";
+ if (args[argidx] == "-noclkinv") {
+ opt_expr_args += " -noclkinv";
continue;
}
if (args[argidx] == "-fine") {
@@ -113,11 +113,19 @@ struct OptPass : public Pass {
}
if (args[argidx] == "-keepdc") {
opt_expr_args += " -keepdc";
- opt_rmdff_args += " -keepdc";
+ opt_dff_args += " -keepdc";
+ continue;
+ }
+ if (args[argidx] == "-nodffe") {
+ opt_dff_args += " -nodffe";
+ continue;
+ }
+ if (args[argidx] == "-nosdff") {
+ opt_dff_args += " -nosdff";
continue;
}
if (args[argidx] == "-sat") {
- opt_rmdff_args += " -sat";
+ opt_dff_args += " -sat";
continue;
}
if (args[argidx] == "-share_all") {
@@ -143,7 +151,7 @@ struct OptPass : public Pass {
Pass::call(design, "opt_merge" + opt_merge_args);
design->scratchpad_unset("opt.did_something");
if (!noff_mode)
- Pass::call(design, "opt_rmdff" + opt_rmdff_args);
+ Pass::call(design, "opt_dff" + opt_dff_args);
if (design->scratchpad_get_bool("opt.did_something") == false)
break;
Pass::call(design, "opt_clean" + opt_clean_args);
@@ -163,7 +171,7 @@ struct OptPass : public Pass {
if (opt_share)
Pass::call(design, "opt_share");
if (!noff_mode)
- Pass::call(design, "opt_rmdff" + opt_rmdff_args);
+ Pass::call(design, "opt_dff" + opt_dff_args);
Pass::call(design, "opt_clean" + opt_clean_args);
Pass::call(design, "opt_expr" + opt_expr_args);
if (design->scratchpad_get_bool("opt.did_something") == false)
diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc
index 44de60b48..883374cf6 100644
--- a/passes/opt/opt_clean.cc
+++ b/passes/opt/opt_clean.cc
@@ -59,6 +59,11 @@ struct keep_cache_t
found_keep = true;
break;
}
+ for (auto wire : module->wires())
+ if (wire->get_bool_attribute(ID::keep)) {
+ found_keep = true;
+ break;
+ }
cache[module] = found_keep;
}
@@ -67,7 +72,7 @@ struct keep_cache_t
bool query(Cell *cell, bool ignore_specify = false)
{
- if (cell->type.in(ID($memwr), ID($meminit), ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
+ if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
return true;
if (!ignore_specify && cell->type.in(ID($specify2), ID($specify3), ID($specrule)))
@@ -90,6 +95,8 @@ int count_rm_cells, count_rm_wires;
void rmunused_module_cells(Module *module, bool verbose)
{
SigMap sigmap(module);
+ dict<IdString, pool<Cell*>> mem2cells;
+ pool<IdString> mem_unused;
pool<Cell*> queue, unused;
pool<SigBit> used_raw_bits;
dict<SigBit, pool<Cell*>> wire2driver;
@@ -103,6 +110,17 @@ void rmunused_module_cells(Module *module, bool verbose)
}
}
+ for (auto &it : module->memories) {
+ mem_unused.insert(it.first);
+ }
+
+ for (Cell *cell : module->cells()) {
+ if (cell->type.in(ID($memwr), ID($meminit))) {
+ IdString mem_id = cell->getParam(ID::MEMID).decode_string();
+ mem2cells[mem_id].insert(cell);
+ }
+ }
+
for (auto &it : module->cells_) {
Cell *cell = it.second;
for (auto &it2 : cell->connections()) {
@@ -140,17 +158,33 @@ void rmunused_module_cells(Module *module, bool verbose)
while (!queue.empty())
{
pool<SigBit> bits;
- for (auto cell : queue)
- for (auto &it : cell->connections())
- if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first))
- for (auto bit : sigmap(it.second))
- bits.insert(bit);
+ pool<IdString> mems;
+ for (auto cell : queue) {
+ for (auto &it : cell->connections())
+ if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first))
+ for (auto bit : sigmap(it.second))
+ bits.insert(bit);
+
+ if (cell->type == ID($memrd)) {
+ IdString mem_id = cell->getParam(ID::MEMID).decode_string();
+ if (mem_unused.count(mem_id)) {
+ mem_unused.erase(mem_id);
+ mems.insert(mem_id);
+ }
+ }
+ }
queue.clear();
+
for (auto bit : bits)
for (auto c : wire2driver[bit])
if (unused.count(c))
queue.insert(c), unused.erase(c);
+
+ for (auto mem : mems)
+ for (auto c : mem2cells[mem])
+ if (unused.count(c))
+ queue.insert(c), unused.erase(c);
}
unused.sort(RTLIL::sort_by_name_id<RTLIL::Cell>());
@@ -163,6 +197,14 @@ void rmunused_module_cells(Module *module, bool verbose)
count_rm_cells++;
}
+ for (auto it : mem_unused)
+ {
+ if (verbose)
+ log_debug(" removing unused memory `%s'.\n", it.c_str());
+ delete module->memories.at(it);
+ module->memories.erase(it);
+ }
+
for (auto &it : module->cells_) {
Cell *cell = it.second;
for (auto &it2 : cell->connections()) {
@@ -202,7 +244,7 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool &regs, SigPoo
if ((w1->port_input && w1->port_output) != (w2->port_input && w2->port_output))
return !(w2->port_input && w2->port_output);
- if (w1->name[0] == '\\' && w2->name[0] == '\\') {
+ if (w1->name.isPublic() && w2->name.isPublic()) {
if (regs.check(s1) != regs.check(s2))
return regs.check(s2);
if (direct_wires.count(w1) != direct_wires.count(w2))
@@ -215,7 +257,7 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool &regs, SigPoo
return w2->port_output;
if (w1->name[0] != w2->name[0])
- return w2->name[0] == '\\';
+ return w2->name.isPublic();
int attrs1 = count_nontrivial_wire_attrs(w1);
int attrs2 = count_nontrivial_wire_attrs(w2);
diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc
new file mode 100644
index 000000000..a47071a30
--- /dev/null
+++ b/passes/opt/opt_dff.cc
@@ -0,0 +1,875 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2020 Marcelina Kościelnicka <mwk@0x04.net>
+ *
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/satgen.h"
+#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
+#include "kernel/ff.h"
+#include "passes/techmap/simplemap.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct OptDffOptions
+{
+ bool nosdff;
+ bool nodffe;
+ bool simple_dffe;
+ bool sat;
+ bool keepdc;
+};
+
+struct OptDffWorker
+{
+ const OptDffOptions &opt;
+
+ Module *module;
+ typedef std::pair<RTLIL::Cell*, int> cell_int_t;
+ SigMap sigmap;
+ FfInitVals initvals;
+ dict<SigBit, int> bitusers;
+ dict<SigBit, cell_int_t> bit2mux;
+ dict<SigBit, RTLIL::Cell*> bit2driver;
+
+ typedef std::map<RTLIL::SigBit, bool> pattern_t;
+ typedef std::set<pattern_t> patterns_t;
+ typedef std::pair<RTLIL::SigBit, bool> ctrl_t;
+ typedef std::set<ctrl_t> ctrls_t;
+
+ ezSatPtr ez;
+ SatGen satgen;
+ pool<Cell*> sat_cells;
+
+ // Used as a queue.
+ std::vector<Cell *> dff_cells;
+
+ OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod), ez(), satgen(ez.get(), &sigmap) {
+ // Gathering three kinds of information here for every sigmapped SigBit:
+ //
+ // - bitusers: how many users it has (muxes will only be merged into FFs if this is 1, making the FF the only user)
+ // - bit2mux: the mux cell and bit index that drives it, if any
+ // - bit2driver: the cell driving it, if any
+
+ for (auto wire : module->wires())
+ {
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ bitusers[bit]++;
+ }
+
+ for (auto cell : module->cells()) {
+ if (cell->type.in(ID($mux), ID($pmux), ID($_MUX_))) {
+ RTLIL::SigSpec sig_y = sigmap(cell->getPort(ID::Y));
+ for (int i = 0; i < GetSize(sig_y); i++)
+ bit2mux[sig_y[i]] = cell_int_t(cell, i);
+ }
+
+ for (auto conn : cell->connections()) {
+ bool is_output = cell->output(conn.first);
+ if (is_output) {
+ for (auto bit : sigmap(conn.second))
+ bit2driver[bit] = cell;
+ }
+ if (!is_output || !cell->known()) {
+ for (auto bit : sigmap(conn.second))
+ bitusers[bit]++;
+ }
+ }
+
+ if (module->design->selected(module, cell) && RTLIL::builtin_ff_cell_types().count(cell->type))
+ dff_cells.push_back(cell);
+ }
+
+ }
+
+ std::function<void(Cell*)> sat_import_cell = [&](Cell *c) {
+ if (!sat_cells.insert(c).second)
+ return;
+ if (!satgen.importCell(c))
+ return;
+ for (auto &conn : c->connections()) {
+ if (!c->input(conn.first))
+ continue;
+ for (auto bit : sigmap(conn.second))
+ if (bit2driver.count(bit))
+ sat_import_cell(bit2driver.at(bit));
+ }
+ };
+
+ State combine_const(State a, State b) {
+ if (a == State::Sx && !opt.keepdc)
+ return b;
+ if (b == State::Sx && !opt.keepdc)
+ return a;
+ if (a == b)
+ return a;
+ return State::Sm;
+ }
+
+ patterns_t find_muxtree_feedback_patterns(RTLIL::SigBit d, RTLIL::SigBit q, pattern_t path)
+ {
+ patterns_t ret;
+
+ if (d == q) {
+ ret.insert(path);
+ return ret;
+ }
+
+ if (bit2mux.count(d) == 0 || bitusers[d] > 1)
+ return ret;
+
+ cell_int_t mbit = bit2mux.at(d);
+ RTLIL::SigSpec sig_a = sigmap(mbit.first->getPort(ID::A));
+ RTLIL::SigSpec sig_b = sigmap(mbit.first->getPort(ID::B));
+ RTLIL::SigSpec sig_s = sigmap(mbit.first->getPort(ID::S));
+ int width = GetSize(sig_a), index = mbit.second;
+
+ for (int i = 0; i < GetSize(sig_s); i++)
+ if (path.count(sig_s[i]) && path.at(sig_s[i]))
+ {
+ ret = find_muxtree_feedback_patterns(sig_b[i*width + index], q, path);
+
+ if (sig_b[i*width + index] == q) {
+ RTLIL::SigSpec s = mbit.first->getPort(ID::B);
+ s[i*width + index] = RTLIL::Sx;
+ mbit.first->setPort(ID::B, s);
+ }
+
+ return ret;
+ }
+
+ pattern_t path_else = path;
+
+ for (int i = 0; i < GetSize(sig_s); i++)
+ {
+ if (path.count(sig_s[i]))
+ continue;
+
+ pattern_t path_this = path;
+ path_else[sig_s[i]] = false;
+ path_this[sig_s[i]] = true;
+
+ for (auto &pat : find_muxtree_feedback_patterns(sig_b[i*width + index], q, path_this))
+ ret.insert(pat);
+
+ if (sig_b[i*width + index] == q) {
+ RTLIL::SigSpec s = mbit.first->getPort(ID::B);
+ s[i*width + index] = RTLIL::Sx;
+ mbit.first->setPort(ID::B, s);
+ }
+ }
+
+ for (auto &pat : find_muxtree_feedback_patterns(sig_a[index], q, path_else))
+ ret.insert(pat);
+
+ if (sig_a[index] == q) {
+ RTLIL::SigSpec s = mbit.first->getPort(ID::A);
+ s[index] = RTLIL::Sx;
+ mbit.first->setPort(ID::A, s);
+ }
+
+ return ret;
+ }
+
+ void simplify_patterns(patterns_t&)
+ {
+ // TBD
+ }
+
+ ctrl_t make_patterns_logic(const patterns_t &patterns, const ctrls_t &ctrls, bool make_gates)
+ {
+ if (patterns.empty() && GetSize(ctrls) == 1) {
+ return *ctrls.begin();
+ }
+
+ RTLIL::SigSpec or_input;
+
+ for (auto pat : patterns)
+ {
+ RTLIL::SigSpec s1, s2;
+ for (auto it : pat) {
+ s1.append(it.first);
+ s2.append(it.second);
+ }
+
+ RTLIL::SigSpec y = module->addWire(NEW_ID);
+ RTLIL::Cell *c = module->addNe(NEW_ID, s1, s2, y);
+
+ if (make_gates) {
+ simplemap(module, c);
+ module->remove(c);
+ }
+
+ or_input.append(y);
+ }
+ for (auto item : ctrls) {
+ if (item.second)
+ or_input.append(item.first);
+ else if (make_gates)
+ or_input.append(module->NotGate(NEW_ID, item.first));
+ else
+ or_input.append(module->Not(NEW_ID, item.first));
+ }
+
+ if (GetSize(or_input) == 0)
+ return ctrl_t(State::S1, true);
+
+ if (GetSize(or_input) == 1)
+ return ctrl_t(or_input, true);
+
+ RTLIL::SigSpec y = module->addWire(NEW_ID);
+ RTLIL::Cell *c = module->addReduceAnd(NEW_ID, or_input, y);
+
+ if (make_gates) {
+ simplemap(module, c);
+ module->remove(c);
+ }
+
+ return ctrl_t(y, true);
+ }
+
+ ctrl_t combine_resets(const ctrls_t &ctrls, bool make_gates)
+ {
+ if (GetSize(ctrls) == 1) {
+ return *ctrls.begin();
+ }
+
+ RTLIL::SigSpec or_input;
+
+ bool final_pol = false;
+ for (auto item : ctrls) {
+ if (item.second)
+ final_pol = true;
+ }
+
+ for (auto item : ctrls) {
+ if (item.second == final_pol)
+ or_input.append(item.first);
+ else if (make_gates)
+ or_input.append(module->NotGate(NEW_ID, item.first));
+ else
+ or_input.append(module->Not(NEW_ID, item.first));
+ }
+
+ RTLIL::SigSpec y = module->addWire(NEW_ID);
+ RTLIL::Cell *c = final_pol ? module->addReduceOr(NEW_ID, or_input, y) : module->addReduceAnd(NEW_ID, or_input, y);
+
+ if (make_gates) {
+ simplemap(module, c);
+ module->remove(c);
+ }
+
+ return ctrl_t(y, final_pol);
+ }
+
+ bool run() {
+ // We have all the information we need, and the list of FFs to process as well. Do it.
+ bool did_something = false;
+ while (!dff_cells.empty()) {
+ Cell *cell = dff_cells.back();
+ dff_cells.pop_back();
+ // Break down the FF into pieces.
+ FfData ff(&initvals, cell);
+ bool changed = false;
+
+ if (!ff.width) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ }
+
+ if (ff.has_sr) {
+ bool sr_removed = false;
+ std::vector<int> keep_bits;
+ // Check for always-active S/R bits.
+ for (int i = 0; i < ff.width; i++) {
+ if (ff.sig_clr[i] == (ff.pol_clr ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_clr[i] == State::Sx)) {
+ // Always-active clear — connect Q bit to 0.
+ initvals.remove_init(ff.sig_q[i]);
+ module->connect(ff.sig_q[i], State::S0);
+ log("Handling always-active CLR at position %d on %s (%s) from module %s (changing to const driver).\n",
+ i, log_id(cell), log_id(cell->type), log_id(module));
+ sr_removed = true;
+ } else if (ff.sig_set[i] == (ff.pol_set ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_set[i] == State::Sx)) {
+ // Always-active set — connect Q bit to 1 if clear inactive, 0 if reset active.
+ initvals.remove_init(ff.sig_q[i]);
+ if (!ff.pol_clr) {
+ module->connect(ff.sig_q[i], ff.sig_clr[i]);
+ } else if (ff.is_fine) {
+ module->addNotGate(NEW_ID, ff.sig_q[i], ff.sig_clr[i]);
+ } else {
+ module->addNot(NEW_ID, ff.sig_q[i], ff.sig_clr[i]);
+ }
+ log("Handling always-active SET at position %d on %s (%s) from module %s (changing to combinatorial circuit).\n",
+ i, log_id(cell), log_id(cell->type), log_id(module));
+ sr_removed = true;
+ } else {
+ keep_bits.push_back(i);
+ }
+ }
+ if (sr_removed) {
+ if (keep_bits.empty()) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ }
+ ff = ff.slice(keep_bits);
+ changed = true;
+ }
+
+ if (ff.pol_clr ? ff.sig_clr.is_fully_zero() : ff.sig_clr.is_fully_ones()) {
+ // CLR is useless, try to kill it.
+ bool failed = false;
+ for (int i = 0; i < ff.width; i++)
+ if (ff.sig_set[i] != ff.sig_set[0])
+ failed = true;
+ if (!failed) {
+ log("Removing never-active CLR on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_sr = false;
+ ff.has_arst = true;
+ ff.pol_arst = ff.pol_set;
+ ff.sig_arst = ff.sig_set[0];
+ ff.val_arst = Const(State::S1, ff.width);
+ changed = true;
+ }
+ } else if (ff.pol_set ? ff.sig_set.is_fully_zero() : ff.sig_set.is_fully_ones()) {
+ // SET is useless, try to kill it.
+ bool failed = false;
+ for (int i = 0; i < ff.width; i++)
+ if (ff.sig_clr[i] != ff.sig_clr[0])
+ failed = true;
+ if (!failed) {
+ log("Removing never-active SET on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_sr = false;
+ ff.has_arst = true;
+ ff.pol_arst = ff.pol_clr;
+ ff.sig_arst = ff.sig_clr[0];
+ ff.val_arst = Const(State::S0, ff.width);
+ changed = true;
+ }
+ } else if (ff.pol_clr == ff.pol_set) {
+ // Try a more complex conversion to plain async reset.
+ State val_neutral = ff.pol_set ? State::S0 : State::S1;
+ Const val_arst;
+ SigSpec sig_arst;
+ if (ff.sig_clr[0] == val_neutral)
+ sig_arst = ff.sig_set[0];
+ else
+ sig_arst = ff.sig_clr[0];
+ bool failed = false;
+ for (int i = 0; i < ff.width; i++) {
+ if (ff.sig_clr[i] == sig_arst && ff.sig_set[i] == val_neutral)
+ val_arst.bits.push_back(State::S0);
+ else if (ff.sig_set[i] == sig_arst && ff.sig_clr[i] == val_neutral)
+ val_arst.bits.push_back(State::S1);
+ else
+ failed = true;
+ }
+ if (!failed) {
+ log("Converting CLR/SET to ARST on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_sr = false;
+ ff.has_arst = true;
+ ff.val_arst = val_arst;
+ ff.sig_arst = sig_arst;
+ ff.pol_arst = ff.pol_clr;
+ changed = true;
+ }
+ }
+ }
+
+ if (ff.has_arst) {
+ if (ff.sig_arst == (ff.pol_arst ? State::S0 : State::S1)) {
+ // Always-inactive reset — remove.
+ log("Removing never-active ARST on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_arst = false;
+ changed = true;
+ } else if (ff.sig_arst == (ff.pol_arst ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_arst == State::Sx)) {
+ // Always-active async reset — change to const driver.
+ log("Handling always-active ARST on %s (%s) from module %s (changing to const driver).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ initvals.remove_init(ff.sig_q);
+ module->remove(cell);
+ module->connect(ff.sig_q, ff.val_arst);
+ did_something = true;
+ continue;
+ }
+ }
+
+ if (ff.has_srst) {
+ if (ff.sig_srst == (ff.pol_srst ? State::S0 : State::S1)) {
+ // Always-inactive reset — remove.
+ log("Removing never-active SRST on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_srst = false;
+ changed = true;
+ } else if (ff.sig_srst == (ff.pol_srst ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_srst == State::Sx)) {
+ // Always-active sync reset — connect to D instead.
+ log("Handling always-active SRST on %s (%s) from module %s (changing to const D).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_srst = false;
+ if (!ff.ce_over_srst)
+ ff.has_en = false;
+ ff.sig_d = ff.val_d = ff.val_srst;
+ ff.d_is_const = true;
+ changed = true;
+ }
+ }
+
+ if (ff.has_en) {
+ if (ff.sig_en == (ff.pol_en ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_en == State::Sx)) {
+ // Always-inactive enable — remove.
+ if (ff.has_clk && ff.has_srst && !ff.ce_over_srst) {
+ log("Handling never-active EN on %s (%s) from module %s (connecting SRST instead).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ // FF with sync reset — connect the sync reset to D instead.
+ ff.pol_en = ff.pol_srst;
+ ff.sig_en = ff.sig_srst;
+ ff.has_srst = false;
+ ff.sig_d = ff.val_d = ff.val_srst;
+ ff.d_is_const = true;
+ changed = true;
+ } else {
+ log("Handling never-active EN on %s (%s) from module %s (removing D path).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ // The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver).
+ ff.has_d = ff.has_en = ff.has_clk = false;
+ changed = true;
+ }
+ } else if (ff.sig_en == (ff.pol_en ? State::S1 : State::S0)) {
+ // Always-active enable.
+ if (ff.has_clk) {
+ // For FF, just remove the useless enable.
+ log("Removing always-active EN on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_en = false;
+ changed = true;
+ } else {
+ // For latches, make a comb circuit, nuke the latch.
+ log("Handling always-active EN on %s (%s) from module %s (changing to combinatorial circuit).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ initvals.remove_init(ff.sig_q);
+ module->remove(cell);
+ if (ff.has_sr) {
+ SigSpec tmp;
+ if (ff.is_fine) {
+ if (ff.pol_set)
+ tmp = module->MuxGate(NEW_ID, ff.sig_d, State::S1, ff.sig_set);
+ else
+ tmp = module->MuxGate(NEW_ID, State::S1, ff.sig_d, ff.sig_set);
+ if (ff.pol_clr)
+ module->addMuxGate(NEW_ID, tmp, State::S0, ff.sig_clr, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, State::S0, tmp, ff.sig_clr, ff.sig_q);
+ } else {
+ if (ff.pol_set)
+ tmp = module->Or(NEW_ID, ff.sig_d, ff.sig_set);
+ else
+ tmp = module->Or(NEW_ID, ff.sig_d, module->Not(NEW_ID, ff.sig_set));
+ if (ff.pol_clr)
+ module->addAnd(NEW_ID, tmp, module->Not(NEW_ID, ff.sig_clr), ff.sig_q);
+ else
+ module->addAnd(NEW_ID, tmp, ff.sig_clr, ff.sig_q);
+ }
+ } else if (ff.has_arst) {
+ if (ff.is_fine) {
+ if (ff.pol_arst)
+ module->addMuxGate(NEW_ID, ff.sig_d, ff.val_arst[0], ff.sig_arst, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, ff.val_arst[0], ff.sig_d, ff.sig_arst, ff.sig_q);
+ } else {
+ if (ff.pol_arst)
+ module->addMux(NEW_ID, ff.sig_d, ff.val_arst, ff.sig_arst, ff.sig_q);
+ else
+ module->addMux(NEW_ID, ff.val_arst, ff.sig_d, ff.sig_arst, ff.sig_q);
+ }
+ } else {
+ module->connect(ff.sig_q, ff.sig_d);
+ }
+ did_something = true;
+ continue;
+ }
+ }
+ }
+
+ if (ff.has_clk) {
+ if (ff.sig_clk.is_fully_const()) {
+ // Const clock — the D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver).
+ log("Handling const CLK on %s (%s) from module %s (removing D path).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_d = ff.has_en = ff.has_clk = ff.has_srst = false;
+ changed = true;
+ }
+ }
+
+ if (ff.has_d && ff.sig_d == ff.sig_q) {
+ // Q wrapped back to D, can be removed.
+ if (ff.has_clk && ff.has_srst) {
+ // FF with sync reset — connect the sync reset to D instead.
+ log("Handling D = Q on %s (%s) from module %s (conecting SRST instead).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ if (ff.has_en && ff.ce_over_srst) {
+ if (!ff.pol_en) {
+ if (ff.is_fine)
+ ff.sig_en = module->NotGate(NEW_ID, ff.sig_en);
+ else
+ ff.sig_en = module->Not(NEW_ID, ff.sig_en);
+ }
+ if (!ff.pol_srst) {
+ if (ff.is_fine)
+ ff.sig_srst = module->NotGate(NEW_ID, ff.sig_srst);
+ else
+ ff.sig_srst = module->Not(NEW_ID, ff.sig_srst);
+ }
+ if (ff.is_fine)
+ ff.sig_en = module->AndGate(NEW_ID, ff.sig_en, ff.sig_srst);
+ else
+ ff.sig_en = module->And(NEW_ID, ff.sig_en, ff.sig_srst);
+ ff.pol_en = true;
+ } else {
+ ff.pol_en = ff.pol_srst;
+ ff.sig_en = ff.sig_srst;
+ }
+ ff.has_en = true;
+ ff.has_srst = false;
+ ff.sig_d = ff.val_d = ff.val_srst;
+ ff.d_is_const = true;
+ changed = true;
+ } else {
+ // The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver).
+ log("Handling D = Q on %s (%s) from module %s (removing D path).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_d = ff.has_en = ff.has_clk = false;
+ changed = true;
+ }
+ }
+
+ // Now check if any bit can be replaced by a constant.
+ pool<int> removed_sigbits;
+ for (int i = 0; i < ff.width; i++) {
+ State val = ff.val_init[i];
+ if (ff.has_arst)
+ val = combine_const(val, ff.val_arst[i]);
+ if (ff.has_srst)
+ val = combine_const(val, ff.val_srst[i]);
+ if (ff.has_sr) {
+ if (ff.sig_clr[i] != (ff.pol_clr ? State::S0 : State::S1))
+ val = combine_const(val, State::S0);
+ if (ff.sig_set[i] != (ff.pol_set ? State::S0 : State::S1))
+ val = combine_const(val, State::S1);
+ }
+ if (val == State::Sm)
+ continue;
+ if (ff.has_d) {
+ if (!ff.sig_d[i].wire) {
+ val = combine_const(val, ff.sig_d[i].data);
+ if (val == State::Sm)
+ continue;
+ } else {
+ if (!opt.sat)
+ continue;
+ // For each register bit, try to prove that it cannot change from the initial value. If so, remove it
+ if (!bit2driver.count(ff.sig_d[i]))
+ continue;
+ if (val != State::S0 && val != State::S1)
+ continue;
+
+ sat_import_cell(bit2driver.at(ff.sig_d[i]));
+
+ int init_sat_pi = satgen.importSigSpec(val).front();
+ int q_sat_pi = satgen.importSigBit(ff.sig_q[i]);
+ int d_sat_pi = satgen.importSigBit(ff.sig_d[i]);
+
+ // Try to find out whether the register bit can change under some circumstances
+ bool counter_example_found = ez->solve(ez->IFF(q_sat_pi, init_sat_pi), ez->NOT(ez->IFF(d_sat_pi, init_sat_pi)));
+
+ // If the register bit cannot change, we can replace it with a constant
+ if (counter_example_found)
+ continue;
+ }
+ }
+ log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", val ? 1 : 0,
+ i, log_id(cell), log_id(cell->type), log_id(module));
+
+ initvals.remove_init(ff.sig_q[i]);
+ module->connect(ff.sig_q[i], val);
+ removed_sigbits.insert(i);
+ }
+ if (!removed_sigbits.empty()) {
+ std::vector<int> keep_bits;
+ for (int i = 0; i < ff.width; i++)
+ if (!removed_sigbits.count(i))
+ keep_bits.push_back(i);
+ if (keep_bits.empty()) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ }
+ ff = ff.slice(keep_bits);
+ changed = true;
+ }
+
+ // The cell has been simplified as much as possible already. Now try to spice it up with enables / sync resets.
+ if (ff.has_clk) {
+ if (!ff.has_arst && !ff.has_sr && (!ff.has_srst || !ff.has_en || ff.ce_over_srst) && !opt.nosdff) {
+ // Try to merge sync resets.
+ std::map<ctrls_t, std::vector<int>> groups;
+ std::vector<int> remaining_indices;
+ Const val_srst;
+
+ for (int i = 0 ; i < ff.width; i++) {
+ ctrls_t resets;
+ State reset_val = State::Sx;
+ if (ff.has_srst)
+ reset_val = ff.val_srst[i];
+ while (bit2mux.count(ff.sig_d[i]) && bitusers[ff.sig_d[i]] == 1) {
+ cell_int_t mbit = bit2mux.at(ff.sig_d[i]);
+ if (GetSize(mbit.first->getPort(ID::S)) != 1)
+ break;
+ SigBit s = mbit.first->getPort(ID::S);
+ SigBit a = mbit.first->getPort(ID::A)[mbit.second];
+ SigBit b = mbit.first->getPort(ID::B)[mbit.second];
+ // Workaround for funny memory WE pattern.
+ if ((a == State::S0 || a == State::S1) && (b == State::S0 || b == State::S1))
+ break;
+ if ((b == State::S0 || b == State::S1) && (b == reset_val || reset_val == State::Sx)) {
+ // This is better handled by CE pattern.
+ if (a == ff.sig_q[i])
+ break;
+ reset_val = b.data;
+ resets.insert(ctrl_t(s, true));
+ ff.sig_d[i] = a;
+ } else if ((a == State::S0 || a == State::S1) && (a == reset_val || reset_val == State::Sx)) {
+ // This is better handled by CE pattern.
+ if (b == ff.sig_q[i])
+ break;
+ reset_val = a.data;
+ resets.insert(ctrl_t(s, false));
+ ff.sig_d[i] = b;
+ } else {
+ break;
+ }
+ }
+
+ if (!resets.empty()) {
+ if (ff.has_srst)
+ resets.insert(ctrl_t(ff.sig_srst, ff.pol_srst));
+ groups[resets].push_back(i);
+ } else
+ remaining_indices.push_back(i);
+ val_srst.bits.push_back(reset_val);
+ }
+
+ for (auto &it : groups) {
+ FfData new_ff = ff.slice(it.second);
+ new_ff.val_srst = Const();
+ for (int i = 0; i < new_ff.width; i++) {
+ int j = it.second[i];
+ new_ff.val_srst.bits.push_back(val_srst[j]);
+ }
+ ctrl_t srst = combine_resets(it.first, ff.is_fine);
+
+ new_ff.has_srst = true;
+ new_ff.sig_srst = srst.first;
+ new_ff.pol_srst = srst.second;
+ if (new_ff.has_en)
+ new_ff.ce_over_srst = true;
+ Cell *new_cell = new_ff.emit(module, NEW_ID);
+ if (new_cell)
+ dff_cells.push_back(new_cell);
+ log("Adding SRST signal on %s (%s) from module %s (D = %s, Q = %s, rval = %s).\n",
+ log_id(cell), log_id(cell->type), log_id(module), log_signal(new_ff.sig_d), log_signal(new_ff.sig_q), log_signal(new_ff.val_srst));
+ }
+
+ if (remaining_indices.empty()) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ } else if (GetSize(remaining_indices) != ff.width) {
+ ff = ff.slice(remaining_indices);
+ changed = true;
+ }
+ }
+ if ((!ff.has_srst || !ff.has_en || !ff.ce_over_srst) && !opt.nodffe) {
+ // Try to merge enables.
+ std::map<std::pair<patterns_t, ctrls_t>, std::vector<int>> groups;
+ std::vector<int> remaining_indices;
+
+ for (int i = 0 ; i < ff.width; i++) {
+ // First, eat up as many simple muxes as possible.
+ ctrls_t enables;
+ while (bit2mux.count(ff.sig_d[i]) && bitusers[ff.sig_d[i]] == 1) {
+ cell_int_t mbit = bit2mux.at(ff.sig_d[i]);
+ if (GetSize(mbit.first->getPort(ID::S)) != 1)
+ break;
+ SigBit s = mbit.first->getPort(ID::S);
+ SigBit a = mbit.first->getPort(ID::A)[mbit.second];
+ SigBit b = mbit.first->getPort(ID::B)[mbit.second];
+ if (a == ff.sig_q[i]) {
+ enables.insert(ctrl_t(s, true));
+ ff.sig_d[i] = b;
+ } else if (b == ff.sig_q[i]) {
+ enables.insert(ctrl_t(s, false));
+ ff.sig_d[i] = a;
+ } else {
+ break;
+ }
+ }
+
+ patterns_t patterns;
+ if (!opt.simple_dffe)
+ patterns = find_muxtree_feedback_patterns(ff.sig_d[i], ff.sig_q[i], pattern_t());
+ if (!patterns.empty() || !enables.empty()) {
+ if (ff.has_en)
+ enables.insert(ctrl_t(ff.sig_en, ff.pol_en));
+ simplify_patterns(patterns);
+ groups[std::make_pair(patterns, enables)].push_back(i);
+ } else
+ remaining_indices.push_back(i);
+ }
+
+ for (auto &it : groups) {
+ FfData new_ff = ff.slice(it.second);
+ ctrl_t en = make_patterns_logic(it.first.first, it.first.second, ff.is_fine);
+
+ new_ff.has_en = true;
+ new_ff.sig_en = en.first;
+ new_ff.pol_en = en.second;
+ new_ff.ce_over_srst = false;
+ Cell *new_cell = new_ff.emit(module, NEW_ID);
+ if (new_cell)
+ dff_cells.push_back(new_cell);
+ log("Adding EN signal on %s (%s) from module %s (D = %s, Q = %s).\n",
+ log_id(cell), log_id(cell->type), log_id(module), log_signal(new_ff.sig_d), log_signal(new_ff.sig_q));
+ }
+
+ if (remaining_indices.empty()) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ } else if (GetSize(remaining_indices) != ff.width) {
+ ff = ff.slice(remaining_indices);
+ changed = true;
+ }
+ }
+ }
+
+ if (changed) {
+ // Rebuild the FF.
+ IdString name = cell->name;
+ module->remove(cell);
+ ff.emit(module, name);
+ did_something = true;
+ }
+ }
+ return did_something;
+ }
+};
+
+struct OptDffPass : public Pass {
+ OptDffPass() : Pass("opt_dff", "perform DFF optimizations") { }
+ void help() override
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] [selection]\n");
+ log("\n");
+ log("This pass converts flip-flops to a more suitable type by merging clock enables\n");
+ log("and synchronous reset multiplexers, removing unused control inputs, or potentially\n");
+ log("removes the flip-flop altogether, converting it to a constant driver.\n");
+ log("\n");
+ log(" -nodffe\n");
+ log(" disables dff -> dffe conversion, and other transforms recognizing clock enable\n");
+ log("\n");
+ log(" -nosdff\n");
+ log(" disables dff -> sdff conversion, and other transforms recognizing sync resets\n");
+ log("\n");
+ log(" -simple-dffe\n");
+ log(" only enables clock enable recognition transform for obvious cases\n");
+ log("\n");
+ log(" -sat\n");
+ log(" additionally invoke SAT solver to detect and remove flip-flops (with\n");
+ log(" non-constant inputs) that can also be replaced with a constant driver\n");
+ log("\n");
+ log(" -keepdc\n");
+ log(" some optimizations change the behavior of the circuit with respect to\n");
+ log(" don't-care bits. for example in 'a+0' a single x-bit in 'a' will cause\n");
+ log(" all result bits to be set to x. this behavior changes when 'a+0' is\n");
+ log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n");
+ log("\n");
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
+ {
+ log_header(design, "Executing OPT_DFF pass (perform DFF optimizations).\n");
+ OptDffOptions opt;
+ opt.nodffe = false;
+ opt.nosdff = false;
+ opt.simple_dffe = false;
+ opt.keepdc = false;
+ opt.sat = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-nodffe") {
+ opt.nodffe = true;
+ continue;
+ }
+ if (args[argidx] == "-nosdff") {
+ opt.nosdff = true;
+ continue;
+ }
+ if (args[argidx] == "-simple-dffe") {
+ opt.simple_dffe = true;
+ continue;
+ }
+ if (args[argidx] == "-keepdc") {
+ opt.keepdc = true;
+ continue;
+ }
+ if (args[argidx] == "-sat") {
+ opt.sat = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ bool did_something = false;
+ for (auto mod : design->selected_modules()) {
+ OptDffWorker worker(opt, mod);
+ if (worker.run())
+ did_something = true;
+ }
+
+ if (did_something)
+ design->scratchpad_set_bool("opt.did_something", true);
+ }
+} OptDffPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
index 649ad83a6..e36e4419d 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -416,7 +416,7 @@ int get_onehot_bit_index(RTLIL::SigSpec signal)
return bit_index;
}
-void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool clkinv)
+void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool noclkinv)
{
if (!design->selected(module))
return;
@@ -465,7 +465,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
#define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0)
#define ACTION_DO_Y(_v_) ACTION_DO(ID::Y, RTLIL::SigSpec(RTLIL::State::S ## _v_))
- if (clkinv)
+ if (!noclkinv)
{
if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memwr)))
handle_polarity_inv(cell, ID::CLK, ID::CLK_POLARITY, assign_map, invert_map);
@@ -604,7 +604,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (cell->type.in(ID($xnor), ID($_XNOR_))) {
cover("opt.opt_expr.const_xnor");
// For consistency since simplemap does $xnor -> $_XOR_ + $_NOT_
- int width = cell->getParam(ID::Y_WIDTH).as_int();
+ int width = GetSize(cell->getPort(ID::Y));
replace_cell(assign_map, module, cell, "const_xnor", ID::Y, SigSpec(RTLIL::State::S1, width));
goto next_cell;
}
@@ -2064,8 +2064,8 @@ struct OptExprPass : public Pass {
log(" -undriven\n");
log(" replace undriven nets with undef (x) constants\n");
log("\n");
- log(" -clkinv\n");
- log(" optimize clock inverters by changing FF types\n");
+ log(" -noclkinv\n");
+ log(" do not optimize clock inverters by changing FF types\n");
log("\n");
log(" -fine\n");
log(" perform fine-grain optimizations\n");
@@ -2085,7 +2085,7 @@ struct OptExprPass : public Pass {
bool mux_undef = false;
bool mux_bool = false;
bool undriven = false;
- bool clkinv = false;
+ bool noclkinv = false;
bool do_fine = false;
bool keepdc = false;
@@ -2106,8 +2106,8 @@ struct OptExprPass : public Pass {
undriven = true;
continue;
}
- if (args[argidx] == "-clkinv") {
- clkinv = true;
+ if (args[argidx] == "-noclkinv") {
+ noclkinv = true;
continue;
}
if (args[argidx] == "-fine") {
@@ -2144,12 +2144,12 @@ struct OptExprPass : public Pass {
do {
do {
did_something = false;
- replace_const_cells(design, module, false /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, clkinv);
+ replace_const_cells(design, module, false /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, noclkinv);
if (did_something)
design->scratchpad_set_bool("opt.did_something", true);
} while (did_something);
if (!keepdc)
- replace_const_cells(design, module, true /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, clkinv);
+ replace_const_cells(design, module, true /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, noclkinv);
if (did_something)
design->scratchpad_set_bool("opt.did_something", true);
} while (did_something);
diff --git a/passes/opt/opt_mem.cc b/passes/opt/opt_mem.cc
index 24df1356b..49a0ac51a 100644
--- a/passes/opt/opt_mem.cc
+++ b/passes/opt/opt_mem.cc
@@ -19,82 +19,11 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/mem.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-struct OptMemWorker
-{
- RTLIL::Design *design;
- RTLIL::Module *module;
- SigMap sigmap;
- bool restart;
-
- dict<IdString, vector<IdString>> memrd, memwr, meminit;
- pool<IdString> remove_mem, remove_cells;
-
- OptMemWorker(RTLIL::Module *module) : design(module->design), module(module), sigmap(module), restart(false)
- {
- for (auto &it : module->memories)
- {
- memrd[it.first];
- memwr[it.first];
- meminit[it.first];
- }
-
- for (auto cell : module->cells())
- {
- if (cell->type == ID($memrd)) {
- IdString id = cell->getParam(ID::MEMID).decode_string();
- memrd.at(id).push_back(cell->name);
- }
-
- if (cell->type == ID($memwr)) {
- IdString id = cell->getParam(ID::MEMID).decode_string();
- memwr.at(id).push_back(cell->name);
- }
-
- if (cell->type == ID($meminit)) {
- IdString id = cell->getParam(ID::MEMID).decode_string();
- meminit.at(id).push_back(cell->name);
- }
- }
- }
-
- ~OptMemWorker()
- {
- for (auto it : remove_mem)
- {
- for (auto cell_name : memrd[it])
- module->remove(module->cell(cell_name));
- for (auto cell_name : memwr[it])
- module->remove(module->cell(cell_name));
- for (auto cell_name : meminit[it])
- module->remove(module->cell(cell_name));
-
- delete module->memories.at(it);
- module->memories.erase(it);
- }
-
- for (auto cell_name : remove_cells)
- module->remove(module->cell(cell_name));
- }
-
- int run(RTLIL::Memory *mem)
- {
- if (restart || remove_mem.count(mem->name))
- return 0;
-
- if (memwr.at(mem->name).empty() && meminit.at(mem->name).empty()) {
- log("Removing memory %s.%s with no write ports or init data.\n", log_id(module), log_id(mem));
- remove_mem.insert(mem->name);
- return 1;
- }
-
- return 0;
- }
-};
-
struct OptMemPass : public Pass {
OptMemPass() : Pass("opt_mem", "optimize memories") { }
void help() override
@@ -122,15 +51,11 @@ struct OptMemPass : public Pass {
int total_count = 0;
for (auto module : design->selected_modules()) {
- while (1) {
- int cnt = 0;
- OptMemWorker worker(module);
- for (auto &it : module->memories)
- if (module->selected(it.second))
- cnt += worker.run(it.second);
- if (!cnt && !worker.restart)
- break;
- total_count += cnt;
+ for (auto &mem : Mem::get_selected_memories(module)) {
+ if (mem.wr_ports.empty() && mem.inits.empty()) {
+ mem.remove();
+ total_count++;
+ }
}
}
diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc
deleted file mode 100644
index 8f7628a4a..000000000
--- a/passes/opt/opt_rmdff.cc
+++ /dev/null
@@ -1,711 +0,0 @@
-/*
- * yosys -- Yosys Open SYnthesis Suite
- *
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
- *
- * 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/log.h"
-#include "kernel/register.h"
-#include "kernel/rtlil.h"
-#include "kernel/satgen.h"
-#include "kernel/sigtools.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-USING_YOSYS_NAMESPACE
-PRIVATE_NAMESPACE_BEGIN
-
-SigMap assign_map, dff_init_map;
-SigSet<RTLIL::Cell*> mux_drivers;
-dict<SigBit, RTLIL::Cell*> bit2driver;
-dict<SigBit, pool<SigBit>> init_attributes;
-
-bool keepdc;
-bool sat;
-
-void remove_init_attr(SigSpec sig)
-{
- for (auto bit : assign_map(sig))
- if (init_attributes.count(bit))
- for (auto wbit : init_attributes.at(bit))
- wbit.wire->attributes.at(ID::init)[wbit.offset] = State::Sx;
-}
-
-bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
-{
- SigSpec sig_set, sig_clr;
- State pol_set, pol_clr;
-
- if (cell->hasPort(ID::S))
- sig_set = cell->getPort(ID::S);
-
- if (cell->hasPort(ID::R))
- sig_clr = cell->getPort(ID::R);
-
- if (cell->hasPort(ID::SET))
- sig_set = cell->getPort(ID::SET);
-
- if (cell->hasPort(ID::CLR))
- sig_clr = cell->getPort(ID::CLR);
-
- log_assert(GetSize(sig_set) == GetSize(sig_clr));
-
- if (cell->type.begins_with("$_DFFSR_")) {
- pol_set = cell->type[9] == 'P' ? State::S1 : State::S0;
- pol_clr = cell->type[10] == 'P' ? State::S1 : State::S0;
- } else
- if (cell->type.begins_with("$_DLATCHSR_")) {
- pol_set = cell->type[12] == 'P' ? State::S1 : State::S0;
- pol_clr = cell->type[13] == 'P' ? State::S1 : State::S0;
- } else
- if (cell->type.in(ID($dffsr), ID($dlatchsr))) {
- pol_set = cell->parameters[ID::SET_POLARITY].as_bool() ? State::S1 : State::S0;
- pol_clr = cell->parameters[ID::CLR_POLARITY].as_bool() ? State::S1 : State::S0;
- } else
- log_abort();
-
- State npol_set = pol_set == State::S0 ? State::S1 : State::S0;
- State npol_clr = pol_clr == State::S0 ? State::S1 : State::S0;
-
- SigSpec sig_d = cell->getPort(ID::D);
- SigSpec sig_q = cell->getPort(ID::Q);
-
- bool did_something = false;
- bool proper_sr = false;
- bool used_pol_set = false;
- bool used_pol_clr = false;
- bool hasreset = false;
- Const reset_val;
- SigSpec sig_reset;
-
- for (int i = 0; i < GetSize(sig_set); i++)
- {
- SigBit s = sig_set[i], c = sig_clr[i];
-
- if (s != npol_set || c != npol_clr)
- hasreset = true;
-
- if (s == pol_set || c == pol_clr)
- {
- log("Constantly %s Q bit %s for SR cell %s (%s) from module %s.\n",
- s == pol_set ? "set" : "cleared", log_signal(sig_q[i]),
- log_id(cell), log_id(cell->type), log_id(mod));
-
- remove_init_attr(sig_q[i]);
- mod->connect(sig_q[i], s == pol_set ? State::S1 : State::S0);
- sig_set.remove(i);
- sig_clr.remove(i);
- sig_d.remove(i);
- sig_q.remove(i--);
- did_something = true;
- continue;
- }
- if (sig_reset.empty() && s.wire != nullptr) sig_reset = s;
- if (sig_reset.empty() && c.wire != nullptr) sig_reset = c;
-
- if (s.wire != nullptr && s != sig_reset) proper_sr = true;
- if (c.wire != nullptr && c != sig_reset) proper_sr = true;
-
- if ((s.wire == nullptr) != (c.wire == nullptr)) {
- if (s.wire != nullptr) used_pol_set = true;
- if (c.wire != nullptr) used_pol_clr = true;
- reset_val.bits.push_back(c.wire == nullptr ? State::S1 : State::S0);
- } else
- proper_sr = true;
- }
-
- if (!hasreset)
- proper_sr = false;
-
- if (GetSize(sig_set) == 0)
- {
- log("Removing %s (%s) from module %s.\n", log_id(cell), log_id(cell->type), log_id(mod));
- mod->remove(cell);
- return true;
- }
-
- if (cell->type.in(ID($dffsr), ID($dlatchsr)))
- {
- cell->setParam(ID::WIDTH, GetSize(sig_d));
- cell->setPort(ID::SET, sig_set);
- cell->setPort(ID::CLR, sig_clr);
- cell->setPort(ID::D, sig_d);
- cell->setPort(ID::Q, sig_q);
- }
- else
- {
- cell->setPort(ID::S, sig_set);
- cell->setPort(ID::R, sig_clr);
- cell->setPort(ID::D, sig_d);
- cell->setPort(ID::Q, sig_q);
- }
-
- if (proper_sr)
- return did_something;
-
- if (used_pol_set && used_pol_clr && pol_set != pol_clr)
- return did_something;
-
- if (cell->type == ID($dlatchsr))
- return did_something;
-
- State unified_pol = used_pol_set ? pol_set : pol_clr;
-
- if (cell->type == ID($dffsr))
- {
- if (hasreset)
- {
- log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), "$adff", log_id(mod));
-
- cell->type = ID($adff);
- cell->setParam(ID::ARST_POLARITY, unified_pol);
- cell->setParam(ID::ARST_VALUE, reset_val);
- cell->setPort(ID::ARST, sig_reset);
-
- cell->unsetParam(ID::SET_POLARITY);
- cell->unsetParam(ID::CLR_POLARITY);
- cell->unsetPort(ID::SET);
- cell->unsetPort(ID::CLR);
- }
- else
- {
- log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), "$dff", log_id(mod));
-
- cell->type = ID($dff);
- cell->unsetParam(ID::SET_POLARITY);
- cell->unsetParam(ID::CLR_POLARITY);
- cell->unsetPort(ID::SET);
- cell->unsetPort(ID::CLR);
- }
-
- return true;
- }
-
- if (!hasreset)
- {
- IdString new_type;
-
- if (cell->type.begins_with("$_DFFSR_"))
- new_type = stringf("$_DFF_%c_", cell->type[8]);
- else if (cell->type.begins_with("$_DLATCHSR_"))
- new_type = stringf("$_DLATCH_%c_", cell->type[11]);
- else
- log_abort();
-
- log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), log_id(new_type), log_id(mod));
-
- cell->type = new_type;
- cell->unsetPort(ID::S);
- cell->unsetPort(ID::R);
-
- return true;
- }
-
- return did_something;
-}
-
-bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
-{
- SigSpec sig_e;
- State on_state, off_state;
-
- if (dlatch->type == ID($dlatch)) {
- sig_e = assign_map(dlatch->getPort(ID::EN));
- on_state = dlatch->getParam(ID::EN_POLARITY).as_bool() ? State::S1 : State::S0;
- off_state = dlatch->getParam(ID::EN_POLARITY).as_bool() ? State::S0 : State::S1;
- } else
- if (dlatch->type == ID($_DLATCH_P_)) {
- sig_e = assign_map(dlatch->getPort(ID::E));
- on_state = State::S1;
- off_state = State::S0;
- } else
- if (dlatch->type == ID($_DLATCH_N_)) {
- sig_e = assign_map(dlatch->getPort(ID::E));
- on_state = State::S0;
- off_state = State::S1;
- } else
- log_abort();
-
- if (sig_e == off_state)
- {
- RTLIL::Const val_init;
- for (auto bit : dff_init_map(dlatch->getPort(ID::Q)))
- val_init.bits.push_back(bit.wire == NULL ? bit.data : State::Sx);
- mod->connect(dlatch->getPort(ID::Q), val_init);
- goto delete_dlatch;
- }
-
- if (sig_e == on_state)
- {
- mod->connect(dlatch->getPort(ID::Q), dlatch->getPort(ID::D));
- goto delete_dlatch;
- }
-
- return false;
-
-delete_dlatch:
- log("Removing %s (%s) from module %s.\n", log_id(dlatch), log_id(dlatch->type), log_id(mod));
- remove_init_attr(dlatch->getPort(ID::Q));
- mod->remove(dlatch);
- return true;
-}
-
-bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
-{
- RTLIL::SigSpec sig_d, sig_q, sig_c, sig_r, sig_e;
- RTLIL::Const val_cp, val_rp, val_rv, val_ep;
-
- if (dff->type == ID($_FF_)) {
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- }
- else if (dff->type == ID($_DFF_N_) || dff->type == ID($_DFF_P_)) {
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- sig_c = dff->getPort(ID::C);
- val_cp = RTLIL::Const(dff->type == ID($_DFF_P_), 1);
- }
- else if (dff->type.begins_with("$_DFF_") && dff->type.compare(9, 1, "_") == 0 &&
- (dff->type[6] == 'N' || dff->type[6] == 'P') &&
- (dff->type[7] == 'N' || dff->type[7] == 'P') &&
- (dff->type[8] == '0' || dff->type[8] == '1')) {
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- sig_c = dff->getPort(ID::C);
- sig_r = dff->getPort(ID::R);
- val_cp = RTLIL::Const(dff->type[6] == 'P', 1);
- val_rp = RTLIL::Const(dff->type[7] == 'P', 1);
- val_rv = RTLIL::Const(dff->type[8] == '1', 1);
- }
- else if (dff->type.begins_with("$_DFFE_") && dff->type.compare(9, 1, "_") == 0 &&
- (dff->type[7] == 'N' || dff->type[7] == 'P') &&
- (dff->type[8] == 'N' || dff->type[8] == 'P')) {
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- sig_c = dff->getPort(ID::C);
- sig_e = dff->getPort(ID::E);
- val_cp = RTLIL::Const(dff->type[7] == 'P', 1);
- val_ep = RTLIL::Const(dff->type[8] == 'P', 1);
- }
- else if (dff->type == ID($ff)) {
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- }
- else if (dff->type == ID($dff)) {
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- sig_c = dff->getPort(ID::CLK);
- val_cp = RTLIL::Const(dff->parameters[ID::CLK_POLARITY].as_bool(), 1);
- }
- else if (dff->type == ID($dffe)) {
- sig_e = dff->getPort(ID::EN);
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- sig_c = dff->getPort(ID::CLK);
- val_cp = RTLIL::Const(dff->parameters[ID::CLK_POLARITY].as_bool(), 1);
- val_ep = RTLIL::Const(dff->parameters[ID::EN_POLARITY].as_bool(), 1);
- }
- else if (dff->type == ID($adff)) {
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- sig_c = dff->getPort(ID::CLK);
- sig_r = dff->getPort(ID::ARST);
- val_cp = RTLIL::Const(dff->parameters[ID::CLK_POLARITY].as_bool(), 1);
- val_rp = RTLIL::Const(dff->parameters[ID::ARST_POLARITY].as_bool(), 1);
- val_rv = dff->parameters[ID::ARST_VALUE];
- }
- else
- log_abort();
-
- assign_map.apply(sig_d);
- assign_map.apply(sig_q);
- assign_map.apply(sig_c);
- assign_map.apply(sig_r);
-
- bool has_init = false;
- RTLIL::Const val_init;
- for (auto bit : dff_init_map(sig_q).to_sigbit_vector()) {
- if (bit.wire == NULL || keepdc)
- has_init = true;
- val_init.bits.push_back(bit.wire == NULL ? bit.data : RTLIL::State::Sx);
- }
-
- if (dff->type.in(ID($ff), ID($dff)) && mux_drivers.has(sig_d)) {
- std::set<RTLIL::Cell*> muxes;
- mux_drivers.find(sig_d, muxes);
- for (auto mux : muxes) {
- RTLIL::SigSpec sig_a = assign_map(mux->getPort(ID::A));
- RTLIL::SigSpec sig_b = assign_map(mux->getPort(ID::B));
- if (sig_a == sig_q && sig_b.is_fully_const() && (!has_init || val_init == sig_b.as_const())) {
- mod->connect(sig_q, sig_b);
- goto delete_dff;
- }
- if (sig_b == sig_q && sig_a.is_fully_const() && (!has_init || val_init == sig_a.as_const())) {
- mod->connect(sig_q, sig_a);
- goto delete_dff;
- }
- }
- }
-
- // If clock is driven by a constant and (i) no reset signal
- // (ii) Q has no initial value
- // (iii) initial value is same as reset value
- if (!sig_c.empty() && sig_c.is_fully_const() && (!sig_r.size() || !has_init || val_init == val_rv)) {
- if (val_rv.bits.size() == 0)
- val_rv = val_init;
- // Q is permanently reset value or initial value
- mod->connect(sig_q, val_rv);
- goto delete_dff;
- }
-
- // If D is fully undefined and reset signal present and (i) Q has no initial value
- // (ii) initial value is same as reset value
- if (sig_d.is_fully_undef() && sig_r.size() && (!has_init || val_init == val_rv)) {
- // Q is permanently reset value
- mod->connect(sig_q, val_rv);
- goto delete_dff;
- }
-
- // If D is fully undefined and no reset signal and Q has an initial value
- if (sig_d.is_fully_undef() && !sig_r.size() && has_init) {
- // Q is permanently initial value
- mod->connect(sig_q, val_init);
- goto delete_dff;
- }
-
- // If D is fully constant and (i) no reset signal
- // (ii) reset value is same as constant D
- // and (a) has no initial value
- // (b) initial value same as constant D
- if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) {
- // Q is permanently D
- mod->connect(sig_q, sig_d);
- goto delete_dff;
- }
-
- // If D input is same as Q output and (i) no reset signal
- // (ii) no initial signal
- // (iii) initial value is same as reset value
- if (sig_d == sig_q && (sig_r.empty() || !has_init || val_init == val_rv)) {
- // Q is permanently reset value or initial value
- if (sig_r.size())
- mod->connect(sig_q, val_rv);
- else if (has_init)
- mod->connect(sig_q, val_init);
- goto delete_dff;
- }
-
- // If reset signal is present, and is fully constant
- if (!sig_r.empty() && sig_r.is_fully_const())
- {
- // If reset value is permanently active or if reset is undefined
- if (sig_r == val_rp || sig_r.is_fully_undef()) {
- // Q is permanently reset value
- mod->connect(sig_q, val_rv);
- goto delete_dff;
- }
-
- log("Removing unused reset from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
-
- if (dff->type == ID($adff)) {
- dff->type = ID($dff);
- dff->unsetPort(ID::ARST);
- dff->unsetParam(ID::ARST_POLARITY);
- dff->unsetParam(ID::ARST_VALUE);
- return true;
- }
-
- log_assert(dff->type.begins_with("$_DFF_"));
- dff->type = stringf("$_DFF_%c_", + dff->type[6]);
- dff->unsetPort(ID::R);
- }
-
- // If enable signal is present, and is fully constant
- if (!sig_e.empty() && sig_e.is_fully_const())
- {
- // If enable value is permanently inactive
- if (sig_e != val_ep) {
- // Q is permanently initial value
- mod->connect(sig_q, val_init);
- goto delete_dff;
- }
-
- log("Removing unused enable from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
-
- if (dff->type == ID($dffe)) {
- dff->type = ID($dff);
- dff->unsetPort(ID::EN);
- dff->unsetParam(ID::EN_POLARITY);
- return true;
- }
-
- log_assert(dff->type.begins_with("$_DFFE_"));
- dff->type = stringf("$_DFF_%c_", + dff->type[7]);
- dff->unsetPort(ID::E);
- }
-
- if (sat && has_init && (!sig_r.size() || val_init == val_rv))
- {
- bool removed_sigbits = false;
-
- ezSatPtr ez;
- SatGen satgen(ez.get(), &assign_map);
- pool<Cell*> sat_cells;
-
- std::function<void(Cell*)> sat_import_cell = [&](Cell *c) {
- if (!sat_cells.insert(c).second)
- return;
- if (!satgen.importCell(c))
- return;
- for (auto &conn : c->connections()) {
- if (!c->input(conn.first))
- continue;
- for (auto bit : assign_map(conn.second))
- if (bit2driver.count(bit))
- sat_import_cell(bit2driver.at(bit));
- }
- };
-
- // For each register bit, try to prove that it cannot change from the initial value. If so, remove it
- for (int position = 0; position < GetSize(sig_d); position += 1) {
- RTLIL::SigBit q_sigbit = sig_q[position];
- RTLIL::SigBit d_sigbit = sig_d[position];
-
- if ((!q_sigbit.wire) || (!d_sigbit.wire))
- continue;
-
- if (!bit2driver.count(d_sigbit))
- continue;
-
- sat_import_cell(bit2driver.at(d_sigbit));
-
- RTLIL::State sigbit_init_val = val_init[position];
- if (sigbit_init_val != State::S0 && sigbit_init_val != State::S1)
- continue;
-
- int init_sat_pi = satgen.importSigSpec(sigbit_init_val).front();
- int q_sat_pi = satgen.importSigBit(q_sigbit);
- int d_sat_pi = satgen.importSigBit(d_sigbit);
-
- // Try to find out whether the register bit can change under some circumstances
- bool counter_example_found = ez->solve(ez->IFF(q_sat_pi, init_sat_pi), ez->NOT(ez->IFF(d_sat_pi, init_sat_pi)));
-
- // If the register bit cannot change, we can replace it with a constant
- if (!counter_example_found)
- {
- log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", sigbit_init_val ? 1 : 0,
- position, log_id(dff), log_id(dff->type), log_id(mod));
-
- SigSpec tmp = dff->getPort(ID::D);
- tmp[position] = sigbit_init_val;
- dff->setPort(ID::D, tmp);
-
- removed_sigbits = true;
- }
- }
-
- if (removed_sigbits) {
- handle_dff(mod, dff);
- return true;
- }
- }
-
-
- return false;
-
-delete_dff:
- log("Removing %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
- remove_init_attr(dff->getPort(ID::Q));
- mod->remove(dff);
-
- for (auto &entry : bit2driver)
- if (entry.second == dff)
- bit2driver.erase(entry.first);
-
- return true;
-}
-
-struct OptRmdffPass : public Pass {
- OptRmdffPass() : Pass("opt_rmdff", "remove DFFs with constant inputs") { }
- void help() override
- {
- // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
- log("\n");
- log(" opt_rmdff [-keepdc] [-sat] [selection]\n");
- log("\n");
- log("This pass identifies flip-flops with constant inputs and replaces them with\n");
- log("a constant driver.\n");
- log("\n");
- log(" -sat\n");
- log(" additionally invoke SAT solver to detect and remove flip-flops (with \n");
- log(" non-constant inputs) that can also be replaced with a constant driver\n");
- log("\n");
- }
- void execute(std::vector<std::string> args, RTLIL::Design *design) override
- {
- int total_count = 0, total_initdrv = 0;
- log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n");
-
- keepdc = false;
- sat = false;
-
- size_t argidx;
- for (argidx = 1; argidx < args.size(); argidx++) {
- if (args[argidx] == "-keepdc") {
- keepdc = true;
- continue;
- }
- if (args[argidx] == "-sat") {
- sat = true;
- continue;
- }
- break;
- }
- extra_args(args, argidx, design);
-
- for (auto module : design->selected_modules()) {
- pool<SigBit> driven_bits;
- dict<SigBit, State> init_bits;
-
- assign_map.set(module);
- dff_init_map.set(module);
- mux_drivers.clear();
- bit2driver.clear();
- init_attributes.clear();
-
- for (auto wire : module->wires())
- {
- if (wire->attributes.count(ID::init) != 0) {
- Const initval = wire->attributes.at(ID::init);
- for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++)
- if (initval[i] == State::S0 || initval[i] == State::S1)
- dff_init_map.add(SigBit(wire, i), initval[i]);
- for (int i = 0; i < GetSize(wire); i++) {
- SigBit wire_bit(wire, i), mapped_bit = assign_map(wire_bit);
- if (mapped_bit.wire) {
- init_attributes[mapped_bit].insert(wire_bit);
- if (i < GetSize(initval))
- init_bits[mapped_bit] = initval[i];
- }
- }
- }
-
- if (wire->port_input) {
- for (auto bit : assign_map(wire))
- driven_bits.insert(bit);
- }
- }
-
- std::vector<RTLIL::IdString> dff_list;
- std::vector<RTLIL::IdString> dffsr_list;
- std::vector<RTLIL::IdString> dlatch_list;
- for (auto cell : module->cells())
- {
- for (auto &conn : cell->connections()) {
- bool is_output = cell->output(conn.first);
- if (is_output || !cell->known())
- for (auto bit : assign_map(conn.second)) {
- if (is_output)
- bit2driver[bit] = cell;
- driven_bits.insert(bit);
- }
- }
-
- if (cell->type.in(ID($mux), ID($pmux))) {
- if (cell->getPort(ID::A).size() == cell->getPort(ID::B).size())
- mux_drivers.insert(assign_map(cell->getPort(ID::Y)), cell);
- continue;
- }
-
- if (!design->selected(module, cell))
- continue;
-
- if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
- ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_), ID($dffsr),
- ID($_DLATCHSR_NNN_), ID($_DLATCHSR_NNP_), ID($_DLATCHSR_NPN_), ID($_DLATCHSR_NPP_),
- ID($_DLATCHSR_PNN_), ID($_DLATCHSR_PNP_), ID($_DLATCHSR_PPN_), ID($_DLATCHSR_PPP_), ID($dlatchsr)))
- dffsr_list.push_back(cell->name);
-
- if (cell->type.in(ID($_FF_), ID($_DFF_N_), ID($_DFF_P_),
- ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
- ID($_DFF_PN0_), ID($_DFF_PN1_), ID($_DFF_PP0_), ID($_DFF_PP1_),
- ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_),
- ID($ff), ID($dff), ID($dffe), ID($adff)))
- dff_list.push_back(cell->name);
-
- if (cell->type.in(ID($dlatch), ID($_DLATCH_P_), ID($_DLATCH_N_)))
- dlatch_list.push_back(cell->name);
- }
-
- for (auto &id : dffsr_list) {
- if (module->cell(id) != nullptr &&
- handle_dffsr(module, module->cells_[id]))
- total_count++;
- }
-
- for (auto &id : dff_list) {
- if (module->cell(id) != nullptr &&
- handle_dff(module, module->cells_[id]))
- total_count++;
- }
-
- for (auto &id : dlatch_list) {
- if (module->cell(id) != nullptr &&
- handle_dlatch(module, module->cells_[id]))
- total_count++;
- }
-
- SigSpec const_init_sigs;
-
- for (auto bit : init_bits)
- if (!driven_bits.count(bit.first))
- const_init_sigs.append(bit.first);
-
- const_init_sigs.sort_and_unify();
-
- for (SigSpec sig : const_init_sigs.chunks())
- {
- Const val;
-
- for (auto bit : sig)
- val.bits.push_back(init_bits.at(bit));
-
- log("Promoting init spec %s = %s to constant driver in module %s.\n",
- log_signal(sig), log_signal(val), log_id(module));
-
- module->connect(sig, val);
- remove_init_attr(sig);
- total_initdrv++;
- }
- }
-
- assign_map.clear();
- mux_drivers.clear();
- bit2driver.clear();
- init_attributes.clear();
-
- if (total_count || total_initdrv)
- design->scratchpad_set_bool("opt.did_something", true);
-
- if (total_initdrv)
- log("Promoted %d init specs to constant drivers.\n", total_initdrv);
-
- if (total_count)
- log("Replaced %d DFF cells.\n", total_count);
- }
-} OptRmdffPass;
-
-PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc
index db21cef28..53296699c 100644
--- a/passes/opt/opt_share.cc
+++ b/passes/opt/opt_share.cc
@@ -30,8 +30,6 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-SigMap assign_map;
-
struct OpMuxConn {
RTLIL::SigSpec sig;
RTLIL::Cell *mux;
@@ -157,9 +155,9 @@ bool decode_port_signed(RTLIL::Cell *cell, RTLIL::IdString port_name)
return false;
}
-ExtSigSpec decode_port(RTLIL::Cell *cell, RTLIL::IdString port_name, SigMap *sigmap)
+ExtSigSpec decode_port(RTLIL::Cell *cell, RTLIL::IdString port_name, const SigMap &sigmap)
{
- auto sig = (*sigmap)(cell->getPort(port_name));
+ auto sig = sigmap(cell->getPort(port_name));
RTLIL::SigSpec sign = decode_port_sign(cell, port_name);
RTLIL::IdString semantics = decode_port_semantics(cell, port_name);
@@ -169,7 +167,7 @@ ExtSigSpec decode_port(RTLIL::Cell *cell, RTLIL::IdString port_name, SigMap *sig
return ExtSigSpec(sig, sign, is_signed, semantics);
}
-void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<OpMuxConn> &ports, const ExtSigSpec &operand)
+void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<OpMuxConn> &ports, const ExtSigSpec &operand, const SigMap &sigmap)
{
std::vector<ExtSigSpec> muxed_operands;
int max_width = 0;
@@ -177,10 +175,10 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
auto op = p.op;
RTLIL::IdString muxed_port_name = ID::A;
- if (decode_port(op, ID::A, &assign_map) == operand)
+ if (decode_port(op, ID::A, sigmap) == operand)
muxed_port_name = ID::B;
- auto operand = decode_port(op, muxed_port_name, &assign_map);
+ auto operand = decode_port(op, muxed_port_name, sigmap);
if (operand.sig.size() > max_width)
max_width = operand.sig.size();
@@ -190,11 +188,13 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
auto shared_op = ports[0].op;
if (std::any_of(muxed_operands.begin(), muxed_operands.end(), [&](ExtSigSpec &op) { return op.sign != muxed_operands[0].sign; }))
- max_width = std::max(max_width, shared_op->getParam(ID::Y_WIDTH).as_int());
-
+ max_width = std::max(max_width, shared_op->getParam(ID::Y_WIDTH).as_int());
- for (auto &operand : muxed_operands)
+ for (auto &operand : muxed_operands) {
operand.sig.extend_u0(max_width, operand.is_signed);
+ if (operand.sign != muxed_operands[0].sign)
+ operand = ExtSigSpec(module->Neg(NEW_ID, operand.sig, operand.is_signed));
+ }
for (const auto& p : ports) {
auto op = p.op;
@@ -203,61 +203,58 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
module->remove(op);
}
- for (auto &muxed_op : muxed_operands)
- if (muxed_op.sign != muxed_operands[0].sign)
- muxed_op = ExtSigSpec(module->Neg(NEW_ID, muxed_op.sig, muxed_op.is_signed));
-
- RTLIL::SigSpec mux_y = mux->getPort(ID::Y);
RTLIL::SigSpec mux_a = mux->getPort(ID::A);
RTLIL::SigSpec mux_b = mux->getPort(ID::B);
RTLIL::SigSpec mux_s = mux->getPort(ID::S);
+ int conn_width = ports[0].sig.size();
+ int conn_mux_offset = ports[0].mux_port_offset;
+ int conn_op_offset = ports[0].op_outsig_offset;
+
RTLIL::SigSpec shared_pmux_a = RTLIL::Const(RTLIL::State::Sx, max_width);
RTLIL::SigSpec shared_pmux_b;
RTLIL::SigSpec shared_pmux_s;
- int conn_width = ports[0].sig.size();
- int conn_offset = ports[0].mux_port_offset;
-
- shared_op->setPort(ID::Y, shared_op->getPort(ID::Y).extract(0, conn_width));
+ // Make a new wire to avoid false equivalence with whatever the former shared output was connected to.
+ Wire *new_out = module->addWire(NEW_ID, conn_op_offset + conn_width);
+ SigSpec new_sig_out = SigSpec(new_out, conn_op_offset, conn_width);
- if (mux->type == ID($pmux)) {
- shared_pmux_s = RTLIL::SigSpec();
-
- for (const auto &p : ports) {
+ for (int i = 0; i < GetSize(ports); i++) {
+ auto &p = ports[i];
+ auto &op = muxed_operands[i];
+ if (p.mux_port_id == GetSize(mux_s)) {
+ shared_pmux_a = op.sig;
+ mux_a.replace(conn_mux_offset, new_sig_out);
+ } else {
shared_pmux_s.append(mux_s[p.mux_port_id]);
- mux_b.replace(p.mux_port_id * mux_a.size() + conn_offset, shared_op->getPort(ID::Y));
+ shared_pmux_b.append(op.sig);
+ mux_b.replace(p.mux_port_id * mux_a.size() + conn_mux_offset, new_sig_out);
}
- } else {
- shared_pmux_s = RTLIL::SigSpec{mux_s, module->Not(NEW_ID, mux_s)};
- mux_a.replace(conn_offset, shared_op->getPort(ID::Y));
- mux_b.replace(conn_offset, shared_op->getPort(ID::Y));
}
mux->setPort(ID::A, mux_a);
mux->setPort(ID::B, mux_b);
- mux->setPort(ID::Y, mux_y);
mux->setPort(ID::S, mux_s);
- for (const auto &op : muxed_operands)
- shared_pmux_b.append(op.sig);
-
- auto mux_to_oper = module->Pmux(NEW_ID, shared_pmux_a, shared_pmux_b, shared_pmux_s);
+ SigSpec mux_to_oper;
+ if (GetSize(shared_pmux_s) == 1) {
+ mux_to_oper = module->Mux(NEW_ID, shared_pmux_a, shared_pmux_b, shared_pmux_s);
+ } else {
+ mux_to_oper = module->Pmux(NEW_ID, shared_pmux_a, shared_pmux_b, shared_pmux_s);
+ }
if (shared_op->type.in(ID($alu))) {
- RTLIL::SigSpec alu_x = shared_op->getPort(ID::X);
- RTLIL::SigSpec alu_co = shared_op->getPort(ID::CO);
-
- shared_op->setPort(ID::X, alu_x.extract(0, conn_width));
- shared_op->setPort(ID::CO, alu_co.extract(0, conn_width));
+ shared_op->setPort(ID::X, module->addWire(NEW_ID, GetSize(new_sig_out)));
+ shared_op->setPort(ID::CO, module->addWire(NEW_ID, GetSize(new_sig_out)));
}
bool is_fine = shared_op->type.in(FINE_BITWISE_OPS);
+ shared_op->setPort(ID::Y, new_out);
if (!is_fine)
- shared_op->setParam(ID::Y_WIDTH, conn_width);
+ shared_op->setParam(ID::Y_WIDTH, GetSize(new_out));
- if (decode_port(shared_op, ID::A, &assign_map) == operand) {
+ if (decode_port(shared_op, ID::A, sigmap) == operand) {
shared_op->setPort(ID::B, mux_to_oper);
if (!is_fine)
shared_op->setParam(ID::B_WIDTH, max_width);
@@ -275,17 +272,7 @@ typedef struct {
} merged_op_t;
-template <typename T> void remove_val(std::vector<T> &v, const std::vector<T> &vals)
-{
- auto val_iter = vals.rbegin();
- for (auto i = v.rbegin(); i != v.rend(); ++i)
- if ((val_iter != vals.rend()) && (*i == *val_iter)) {
- v.erase(i.base() - 1);
- ++val_iter;
- }
-}
-
-void check_muxed_operands(std::vector<const OpMuxConn *> &ports, const ExtSigSpec &shared_operand)
+void check_muxed_operands(std::vector<const OpMuxConn *> &ports, const ExtSigSpec &shared_operand, const SigMap &sigmap)
{
auto it = ports.begin();
ExtSigSpec seed;
@@ -295,11 +282,11 @@ void check_muxed_operands(std::vector<const OpMuxConn *> &ports, const ExtSigSpe
auto op = p->op;
RTLIL::IdString muxed_port_name = ID::A;
- if (decode_port(op, ID::A, &assign_map) == shared_operand) {
+ if (decode_port(op, ID::A, sigmap) == shared_operand) {
muxed_port_name = ID::B;
}
- auto operand = decode_port(op, muxed_port_name, &assign_map);
+ auto operand = decode_port(op, muxed_port_name, sigmap);
if (seed.empty())
seed = operand;
@@ -312,7 +299,7 @@ void check_muxed_operands(std::vector<const OpMuxConn *> &ports, const ExtSigSpe
}
}
-ExtSigSpec find_shared_operand(const OpMuxConn* seed, std::vector<const OpMuxConn *> &ports, const std::map<ExtSigSpec, std::set<RTLIL::Cell *>> &operand_to_users)
+ExtSigSpec find_shared_operand(const OpMuxConn* seed, std::vector<const OpMuxConn *> &ports, const std::map<ExtSigSpec, std::set<RTLIL::Cell *>> &operand_to_users, const SigMap &sigmap)
{
std::set<RTLIL::Cell *> ops_using_operand;
std::set<RTLIL::Cell *> ops_set;
@@ -324,7 +311,7 @@ ExtSigSpec find_shared_operand(const OpMuxConn* seed, std::vector<const OpMuxCon
auto op_a = seed->op;
for (RTLIL::IdString port_name : {ID::A, ID::B}) {
- oper = decode_port(op_a, port_name, &assign_map);
+ oper = decode_port(op_a, port_name, sigmap);
auto operand_users = operand_to_users.at(oper);
if (operand_users.size() == 1)
@@ -345,132 +332,6 @@ ExtSigSpec find_shared_operand(const OpMuxConn* seed, std::vector<const OpMuxCon
return ExtSigSpec();
}
-dict<RTLIL::SigSpec, OpMuxConn> find_valid_op_mux_conns(RTLIL::Module *module, dict<RTLIL::SigBit, RTLIL::SigSpec> &op_outbit_to_outsig,
- dict<RTLIL::SigSpec, RTLIL::Cell *> outsig_to_operator,
- dict<RTLIL::SigBit, RTLIL::SigSpec> &op_aux_to_outsig)
-{
- dict<RTLIL::SigSpec, int> op_outsig_user_track;
- dict<RTLIL::SigSpec, OpMuxConn> op_mux_conn_map;
-
- std::function<void(RTLIL::SigSpec)> remove_outsig = [&](RTLIL::SigSpec outsig) {
- for (auto op_outbit : outsig)
- op_outbit_to_outsig.erase(op_outbit);
-
- if (op_mux_conn_map.count(outsig))
- op_mux_conn_map.erase(outsig);
- };
-
- std::function<void(RTLIL::SigBit)> remove_outsig_from_aux_bit = [&](RTLIL::SigBit auxbit) {
- auto aux_outsig = op_aux_to_outsig.at(auxbit);
- auto op = outsig_to_operator.at(aux_outsig);
- auto op_outsig = assign_map(op->getPort(ID::Y));
- remove_outsig(op_outsig);
-
- for (auto aux_outbit : aux_outsig)
- op_aux_to_outsig.erase(aux_outbit);
- };
-
- std::function<void(RTLIL::Cell *)> find_op_mux_conns = [&](RTLIL::Cell *mux) {
- RTLIL::SigSpec sig;
- int mux_port_size;
-
- if (mux->type.in(ID($mux), ID($_MUX_))) {
- mux_port_size = mux->getPort(ID::A).size();
- sig = RTLIL::SigSpec{mux->getPort(ID::B), mux->getPort(ID::A)};
- } else {
- mux_port_size = mux->getPort(ID::A).size();
- sig = mux->getPort(ID::B);
- }
-
- auto mux_insig = assign_map(sig);
-
- for (int i = 0; i < mux_insig.size(); ++i) {
- if (op_aux_to_outsig.count(mux_insig[i])) {
- remove_outsig_from_aux_bit(mux_insig[i]);
- continue;
- }
-
- if (!op_outbit_to_outsig.count(mux_insig[i]))
- continue;
-
- auto op_outsig = op_outbit_to_outsig.at(mux_insig[i]);
-
- if (op_mux_conn_map.count(op_outsig)) {
- remove_outsig(op_outsig);
- continue;
- }
-
- int mux_port_id = i / mux_port_size;
- int mux_port_offset = i % mux_port_size;
-
- int op_outsig_offset;
- for (op_outsig_offset = 0; op_outsig[op_outsig_offset] != mux_insig[i]; ++op_outsig_offset)
- ;
-
- int j = op_outsig_offset;
- do {
- if (!op_outbit_to_outsig.count(mux_insig[i]))
- break;
-
- if (op_outbit_to_outsig.at(mux_insig[i]) != op_outsig)
- break;
-
- ++i;
- ++j;
- } while ((i / mux_port_size == mux_port_id) && (j < op_outsig.size()));
-
- int op_conn_width = j - op_outsig_offset;
- OpMuxConn inp = {
- op_outsig.extract(op_outsig_offset, op_conn_width),
- mux,
- outsig_to_operator.at(op_outsig),
- mux_port_id,
- mux_port_offset,
- op_outsig_offset,
- };
-
- op_mux_conn_map[op_outsig] = inp;
-
- --i;
- }
- };
-
- std::function<void(RTLIL::SigSpec)> remove_connected_ops = [&](RTLIL::SigSpec sig) {
- auto mux_insig = assign_map(sig);
- for (auto outbit : mux_insig) {
- if (op_aux_to_outsig.count(outbit)) {
- remove_outsig_from_aux_bit(outbit);
- continue;
- }
-
- if (!op_outbit_to_outsig.count(outbit))
- continue;
-
- remove_outsig(op_outbit_to_outsig.at(outbit));
- }
- };
-
- for (auto cell : module->cells()) {
- if (cell->type.in(ID($mux), ID($_MUX_), ID($pmux))) {
- remove_connected_ops(cell->getPort(ID::S));
- find_op_mux_conns(cell);
- } else {
- for (auto &conn : cell->connections())
- if (cell->input(conn.first))
- remove_connected_ops(conn.second);
- }
- }
-
- for (auto w : module->wires()) {
- if (!w->port_output)
- continue;
-
- remove_connected_ops(w);
- }
-
- return op_mux_conn_map;
-}
-
struct OptSharePass : public Pass {
OptSharePass() : Pass("opt_share", "merge mutually exclusive cells of the same type that share an input signal") {}
void help() override
@@ -495,37 +356,46 @@ struct OptSharePass : public Pass {
extra_args(args, 1, design);
for (auto module : design->selected_modules()) {
- assign_map.clear();
- assign_map.set(module);
+ SigMap sigmap(module);
+
+ dict<RTLIL::SigBit, int> bit_users;
+
+ for (auto cell : module->cells())
+ for (auto conn : cell->connections())
+ for (auto bit : conn.second)
+ bit_users[sigmap(bit)]++;
+
+ for (auto wire : module->wires())
+ if (wire->port_id != 0)
+ for (auto bit : SigSpec(wire))
+ bit_users[sigmap(bit)]++;
std::map<ExtSigSpec, std::set<RTLIL::Cell *>> operand_to_users;
- dict<RTLIL::SigSpec, RTLIL::Cell *> outsig_to_operator;
- dict<RTLIL::SigBit, RTLIL::SigSpec> op_outbit_to_outsig;
- dict<RTLIL::SigBit, RTLIL::SigSpec> op_aux_to_outsig;
+ dict<RTLIL::SigBit, std::pair<RTLIL::Cell *, int>> op_outbit_to_outsig;
bool any_shared_operands = false;
- std::vector<ExtSigSpec> op_insigs;
- for (auto cell : module->cells()) {
+ for (auto cell : module->selected_cells()) {
if (!cell_supported(cell))
continue;
+ bool skip = false;
if (cell->type == ID($alu)) {
for (RTLIL::IdString port_name : {ID::X, ID::CO}) {
- auto mux_insig = assign_map(cell->getPort(port_name));
- outsig_to_operator[mux_insig] = cell;
- for (auto outbit : mux_insig)
- op_aux_to_outsig[outbit] = mux_insig;
+ for (auto outbit : sigmap(cell->getPort(port_name)))
+ if (bit_users[outbit] > 1)
+ skip = true;
}
}
- auto mux_insig = assign_map(cell->getPort(ID::Y));
- outsig_to_operator[mux_insig] = cell;
- for (auto outbit : mux_insig)
- op_outbit_to_outsig[outbit] = mux_insig;
+ if (skip)
+ continue;
+
+ auto mux_insig = sigmap(cell->getPort(ID::Y));
+ for (int i = 0; i < GetSize(mux_insig); i++)
+ op_outbit_to_outsig[mux_insig[i]] = std::make_pair(cell, i);
for (RTLIL::IdString port_name : {ID::A, ID::B}) {
- auto op_insig = decode_port(cell, port_name, &assign_map);
- op_insigs.push_back(op_insig);
+ auto op_insig = decode_port(cell, port_name, sigmap);
operand_to_users[op_insig].insert(cell);
if (operand_to_users[op_insig].size() > 1)
any_shared_operands = true;
@@ -537,34 +407,79 @@ struct OptSharePass : public Pass {
// Operator outputs need to be exclusively connected to the $mux inputs in order to be mergeable. Hence we count to
// how many points are operator output bits connected.
- dict<RTLIL::SigSpec, OpMuxConn> op_mux_conn_map =
- find_valid_op_mux_conns(module, op_outbit_to_outsig, outsig_to_operator, op_aux_to_outsig);
+ std::vector<merged_op_t> merged_ops;
- // Group op connections connected to same ports of the same $mux. Sort them in ascending order of their port offset
- dict<RTLIL::Cell*, std::vector<std::set<OpMuxConn>>> mux_port_op_conns;
- for (auto& val: op_mux_conn_map) {
- OpMuxConn p = val.second;
- auto& mux_port_conns = mux_port_op_conns[p.mux];
+ for (auto mux : module->selected_cells()) {
+ if (!mux->type.in(ID($mux), ID($_MUX_), ID($pmux)))
+ continue;
- if (mux_port_conns.size() == 0) {
- int mux_port_num;
+ int mux_port_size = GetSize(mux->getPort(ID::A));
+ int mux_port_num = GetSize(mux->getPort(ID::S)) + 1;
- if (p.mux->type.in(ID($mux), ID($_MUX_)))
- mux_port_num = 2;
- else
- mux_port_num = p.mux->getPort(ID::S).size();
+ RTLIL::SigSpec mux_insig = sigmap(RTLIL::SigSpec{mux->getPort(ID::B), mux->getPort(ID::A)});
+ std::vector<std::set<OpMuxConn>> mux_port_conns(mux_port_num);
+ int found = 0;
- mux_port_conns.resize(mux_port_num);
- }
+ for (int mux_port_id = 0; mux_port_id < mux_port_num; mux_port_id++) {
+ SigSpec mux_insig;
+ if (mux_port_id == mux_port_num - 1) {
+ mux_insig = sigmap(mux->getPort(ID::A));
+ } else {
+ mux_insig = sigmap(mux->getPort(ID::B).extract(mux_port_id * mux_port_size, mux_port_size));
+ }
- mux_port_conns[p.mux_port_id].insert(p);
- }
+ for (int mux_port_offset = 0; mux_port_offset < mux_port_size; ++mux_port_offset) {
+ if (!op_outbit_to_outsig.count(mux_insig[mux_port_offset]))
+ continue;
- std::vector<merged_op_t> merged_ops;
- for (auto& val: mux_port_op_conns) {
+ RTLIL::Cell *cell;
+ int op_outsig_offset;
+ std::tie(cell, op_outsig_offset) = op_outbit_to_outsig.at(mux_insig[mux_port_offset]);
+ SigSpec op_outsig = sigmap(cell->getPort(ID::Y));
+ int op_outsig_size = GetSize(op_outsig);
+ int op_conn_width = 0;
+
+ while (mux_port_offset + op_conn_width < mux_port_size &&
+ op_outsig_offset + op_conn_width < op_outsig_size &&
+ mux_insig[mux_port_offset + op_conn_width] == op_outsig[op_outsig_offset + op_conn_width])
+ op_conn_width++;
+
+ log_assert(op_conn_width >= 1);
+
+ bool skip = false;
+ for (int i = 0; i < op_outsig_size; i++) {
+ int expected = 1;
+ if (i >= op_outsig_offset && i < op_outsig_offset + op_conn_width)
+ expected = 2;
+ if (bit_users[op_outsig[i]] != expected)
+ skip = true;
+ }
+ if (skip) {
+ mux_port_offset += op_conn_width;
+ mux_port_offset--;
+ continue;
+ }
+
+ OpMuxConn inp = {
+ op_outsig.extract(op_outsig_offset, op_conn_width),
+ mux,
+ cell,
+ mux_port_id,
+ mux_port_offset,
+ op_outsig_offset,
+ };
+
+ mux_port_conns[mux_port_id].insert(inp);
+
+ mux_port_offset += op_conn_width;
+ mux_port_offset--;
- RTLIL::Cell* cell = val.first;
- auto &mux_port_conns = val.second;
+ found++;
+ }
+ }
+
+ if (found < 2)
+ continue;
const OpMuxConn *seed = NULL;
@@ -612,12 +527,12 @@ struct OptSharePass : public Pass {
continue;
// Filter mergeable connections whose ops share an operand with seed connection's op
- auto shared_operand = find_shared_operand(seed, mergeable_conns, operand_to_users);
+ auto shared_operand = find_shared_operand(seed, mergeable_conns, operand_to_users, sigmap);
if (shared_operand.empty())
continue;
- check_muxed_operands(mergeable_conns, shared_operand);
+ check_muxed_operands(mergeable_conns, shared_operand, sigmap);
if (mergeable_conns.size() < 2)
continue;
@@ -631,7 +546,7 @@ struct OptSharePass : public Pass {
seed = NULL;
- merged_ops.push_back(merged_op_t{cell, merged_ports, shared_operand});
+ merged_ops.push_back(merged_op_t{mux, merged_ports, shared_operand});
design->scratchpad_set_bool("opt.did_something", true);
}
@@ -647,7 +562,7 @@ struct OptSharePass : public Pass {
log(" %s\n", log_id(op.op));
log("\n");
- merge_operators(module, shared.mux, shared.ports, shared.shared_operand);
+ merge_operators(module, shared.mux, shared.ports, shared.shared_operand, sigmap);
}
}
}
diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc
index 9f226e12d..f3b1fd377 100644
--- a/passes/opt/pmux2shiftx.cc
+++ b/passes/opt/pmux2shiftx.cc
@@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -30,7 +31,7 @@ struct OnehotDatabase
bool verbose = false;
bool initialized = false;
- pool<SigBit> init_ones;
+ FfInitVals initvals;
dict<SigSpec, pool<SigSpec>> sig_sources_db;
dict<SigSpec, bool> sig_onehot_cache;
pool<SigSpec> recursion_guard;
@@ -44,19 +45,7 @@ struct OnehotDatabase
log_assert(!initialized);
initialized = true;
- for (auto wire : module->wires())
- {
- auto it = wire->attributes.find(ID::init);
- if (it == wire->attributes.end())
- continue;
-
- auto &val = it->second;
- int width = std::max(GetSize(wire), GetSize(val));
-
- for (int i = 0; i < width; i++)
- if (val[i] == State::S1)
- init_ones.insert(sigmap(SigBit(wire, i)));
- }
+ initvals.set(&sigmap, module);
for (auto cell : module->cells())
{
@@ -119,7 +108,7 @@ struct OnehotDatabase
bool found_init_ones = false;
for (auto bit : sig) {
- if (init_ones.count(bit)) {
+ if (initvals(bit) == State::S1) {
if (found_init_ones) {
if (verbose)
log("%*s - non-onehot init value\n", indent, "");
diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc
index 78e2bcbea..a216f36d4 100644
--- a/passes/opt/wreduce.cc
+++ b/passes/opt/wreduce.cc
@@ -20,6 +20,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/modtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
@@ -54,8 +55,7 @@ struct WreduceWorker
std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells;
std::set<SigBit> work_queue_bits;
pool<SigBit> keep_bits;
- dict<SigBit, State> init_bits;
- pool<SigBit> remove_init_bits;
+ FfInitVals initvals;
WreduceWorker(WreduceConfig *config, Module *module) :
config(config), module(module), mi(module) { }
@@ -145,7 +145,7 @@ struct WreduceWorker
SigSpec sig_d = mi.sigmap(cell->getPort(ID::D));
SigSpec sig_q = mi.sigmap(cell->getPort(ID::Q));
bool has_reset = false;
- Const initval, rst_value;
+ Const initval = initvals(sig_q), rst_value;
int width_before = GetSize(sig_q);
@@ -163,20 +163,12 @@ struct WreduceWorker
bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
bool sign_ext = !zero_ext;
- for (int i = 0; i < GetSize(sig_q); i++) {
- SigBit bit = sig_q[i];
- if (init_bits.count(bit))
- initval.bits.push_back(init_bits.at(bit));
- else
- initval.bits.push_back(State::Sx);
- }
-
for (int i = GetSize(sig_q)-1; i >= 0; i--)
{
if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx) &&
(!has_reset || i >= GetSize(rst_value) || rst_value[i] == State::S0 || rst_value[i] == State::Sx)) {
module->connect(sig_q[i], State::S0);
- remove_init_bits.insert(sig_q[i]);
+ initvals.remove_init(sig_q[i]);
sig_d.remove(i);
sig_q.remove(i);
continue;
@@ -185,7 +177,7 @@ struct WreduceWorker
if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1] &&
(!has_reset || i >= GetSize(rst_value) || rst_value[i] == rst_value[i-1])) {
module->connect(sig_q[i], sig_q[i-1]);
- remove_init_bits.insert(sig_q[i]);
+ initvals.remove_init(sig_q[i]);
sig_d.remove(i);
sig_q.remove(i);
continue;
@@ -195,7 +187,7 @@ struct WreduceWorker
if (info == nullptr)
return;
if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
- remove_init_bits.insert(sig_q[i]);
+ initvals.remove_init(sig_q[i]);
sig_d.remove(i);
sig_q.remove(i);
zero_ext = false;
@@ -409,18 +401,12 @@ struct WreduceWorker
{
// create a copy as mi.sigmap will be updated as we process the module
SigMap init_attr_sigmap = mi.sigmap;
+ initvals.set(&init_attr_sigmap, module);
for (auto w : module->wires()) {
if (w->get_bool_attribute(ID::keep))
for (auto bit : mi.sigmap(w))
keep_bits.insert(bit);
- if (w->attributes.count(ID::init)) {
- Const initval = w->attributes.at(ID::init);
- SigSpec initsig = init_attr_sigmap(w);
- int width = std::min(GetSize(initval), GetSize(initsig));
- for (int i = 0; i < width; i++)
- init_bits[initsig[i]] = initval[i];
- }
}
for (auto c : module->selected_cells())
@@ -469,22 +455,6 @@ struct WreduceWorker
module->connect(nw, SigSpec(w).extract(0, GetSize(nw)));
module->swap_names(w, nw);
}
-
- if (!remove_init_bits.empty()) {
- for (auto w : module->wires()) {
- if (w->attributes.count(ID::init)) {
- Const initval = w->attributes.at(ID::init);
- Const new_initval(State::Sx, GetSize(w));
- SigSpec initsig = init_attr_sigmap(w);
- int width = std::min(GetSize(initval), GetSize(initsig));
- for (int i = 0; i < width; i++) {
- if (!remove_init_bits.count(initsig[i]))
- new_initval[i] = initval[i];
- }
- w->attributes.at(ID::init) = new_initval;
- }
- }
- }
}
};
diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc
index 1a57bef7d..c6bbc386a 100644
--- a/passes/pmgen/Makefile.inc
+++ b/passes/pmgen/Makefile.inc
@@ -36,7 +36,6 @@ $(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h))
PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul.pmg
PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
-PEEPOPT_PATTERN += passes/pmgen/peepopt_dffmux.pmg
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
$(P) mkdir -p passes/pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^)
diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc
index fff04074b..c46f5d58f 100644
--- a/passes/pmgen/ice40_dsp.cc
+++ b/passes/pmgen/ice40_dsp.cc
@@ -31,15 +31,15 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul));
- log_debug("ffA: %s %s %s\n", log_id(st.ffA, "--"), log_id(st.ffAholdmux, "--"), log_id(st.ffArstmux, "--"));
- log_debug("ffB: %s %s %s\n", log_id(st.ffB, "--"), log_id(st.ffBholdmux, "--"), log_id(st.ffBrstmux, "--"));
- log_debug("ffCD: %s %s\n", log_id(st.ffCD, "--"), log_id(st.ffCDholdmux, "--"));
+ log_debug("ffA: %s\n", log_id(st.ffA, "--"));
+ log_debug("ffB: %s\n", log_id(st.ffB, "--"));
+ log_debug("ffCD: %s\n", log_id(st.ffCD, "--"));
log_debug("mul: %s\n", log_id(st.mul, "--"));
log_debug("ffFJKG: %s\n", log_id(st.ffFJKG, "--"));
log_debug("ffH: %s\n", log_id(st.ffH, "--"));
log_debug("add: %s\n", log_id(st.add, "--"));
log_debug("mux: %s\n", log_id(st.mux, "--"));
- log_debug("ffO: %s %s %s\n", log_id(st.ffO, "--"), log_id(st.ffOholdmux, "--"), log_id(st.ffOrstmux, "--"));
+ log_debug("ffO: %s\n", log_id(st.ffO, "--"));
log_debug("\n");
if (GetSize(st.sigA) > 16) {
@@ -97,16 +97,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setParam(ID(D_REG), st.ffCD ? State::S1 : State::S0);
SigSpec AHOLD, BHOLD, CDHOLD;
- if (st.ffAholdmux)
- AHOLD = st.ffAholdpol ? st.ffAholdmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffAholdmux->getPort(ID::S));
+ if (st.ffA && st.ffA->hasPort(ID::EN))
+ AHOLD = st.ffA->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffA->getPort(ID::EN)) : st.ffA->getPort(ID::EN);
else
AHOLD = State::S0;
- if (st.ffBholdmux)
- BHOLD = st.ffBholdpol ? st.ffBholdmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffBholdmux->getPort(ID::S));
+ if (st.ffB && st.ffB->hasPort(ID::EN))
+ BHOLD = st.ffB->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffB->getPort(ID::EN)) : st.ffB->getPort(ID::EN);
else
BHOLD = State::S0;
- if (st.ffCDholdmux)
- CDHOLD = st.ffCDholdpol ? st.ffCDholdmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffCDholdmux->getPort(ID::S));
+ if (st.ffCD && st.ffCD->hasPort(ID::EN))
+ CDHOLD = st.ffCD->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffCD->getPort(ID::EN)) : st.ffCD->getPort(ID::EN);
else
CDHOLD = State::S0;
cell->setPort(ID(AHOLD), AHOLD);
@@ -115,12 +115,12 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort(ID(DHOLD), CDHOLD);
SigSpec IRSTTOP, IRSTBOT;
- if (st.ffArstmux)
- IRSTTOP = st.ffArstpol ? st.ffArstmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffArstmux->getPort(ID::S));
+ if (st.ffA && st.ffA->hasPort(ID::ARST))
+ IRSTTOP = st.ffA->getParam(ID::ARST_POLARITY).as_bool() ? st.ffA->getPort(ID::ARST) : pm.module->Not(NEW_ID, st.ffA->getPort(ID::ARST));
else
IRSTTOP = State::S0;
- if (st.ffBrstmux)
- IRSTBOT = st.ffBrstpol ? st.ffBrstmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffBrstmux->getPort(ID::S));
+ if (st.ffB && st.ffB->hasPort(ID::ARST))
+ IRSTBOT = st.ffB->getParam(ID::ARST_POLARITY).as_bool() ? st.ffB->getPort(ID::ARST) : pm.module->Not(NEW_ID, st.ffB->getPort(ID::ARST));
else
IRSTBOT = State::S0;
cell->setPort(ID(IRSTTOP), IRSTTOP);
@@ -207,16 +207,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
}
SigSpec OHOLD;
- if (st.ffOholdmux)
- OHOLD = st.ffOholdpol ? st.ffOholdmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffOholdmux->getPort(ID::S));
+ if (st.ffO && st.ffO->hasPort(ID::EN))
+ OHOLD = st.ffO->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffO->getPort(ID::EN)) : st.ffO->getPort(ID::EN);
else
OHOLD = State::S0;
cell->setPort(ID(OHOLDTOP), OHOLD);
cell->setPort(ID(OHOLDBOT), OHOLD);
SigSpec ORST;
- if (st.ffOrstmux)
- ORST = st.ffOrstpol ? st.ffOrstmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffOrstmux->getPort(ID::S));
+ if (st.ffO && st.ffO->hasPort(ID::ARST))
+ ORST = st.ffO->getParam(ID::ARST_POLARITY).as_bool() ? st.ffO->getPort(ID::ARST) : pm.module->Not(NEW_ID, st.ffO->getPort(ID::ARST));
else
ORST = State::S0;
cell->setPort(ID(ORSTTOP), ORST);
@@ -228,6 +228,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
acc_reset = st.mux->getPort(ID::S);
else
acc_reset = pm.module->Not(NEW_ID, st.mux->getPort(ID::S));
+ } else if (st.ffO && st.ffO->hasPort(ID::SRST)) {
+ acc_reset = st.ffO->getParam(ID::SRST_POLARITY).as_bool() ? st.ffO->getPort(ID::SRST) : pm.module->Not(NEW_ID, st.ffO->getPort(ID::SRST));
}
cell->setPort(ID(OLOADTOP), acc_reset);
cell->setPort(ID(OLOADBOT), acc_reset);
diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg
index 2456a49dc..7a01cbd51 100644
--- a/passes/pmgen/ice40_dsp.pmg
+++ b/passes/pmgen/ice40_dsp.pmg
@@ -6,20 +6,16 @@ state <SigSpec> sigA sigB sigCD sigH sigO
state <Cell*> add mux
state <IdString> addAB muxAB
-state <bool> ffAholdpol ffBholdpol ffCDholdpol ffOholdpol
-state <bool> ffArstpol ffBrstpol ffCDrstpol ffOrstpol
-
-state <Cell*> ffA ffAholdmux ffArstmux ffB ffBholdmux ffBrstmux ffCD ffCDholdmux
-state <Cell*> ffFJKG ffH ffO ffOholdmux ffOrstmux
+state <Cell*> ffA ffB ffCD
+state <Cell*> ffFJKG ffH ffO
// subpattern
+state <bool> argSdff
state <SigSpec> argQ argD
-state <bool> ffholdpol ffrstpol
-state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
-udata <Cell*> dff dffholdmux dffrstmux
-udata <bool> dffholdpol dffrstpol dffclock_pol
+udata <Cell*> dff
+udata <bool> dffclock_pol
match mul
select mul->type.in($mul, \SB_MAC16)
@@ -64,7 +60,7 @@ code sigA sigB sigH
log_assert(nusers(O.extract_end(i)) <= 1);
endcode
-code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
+code argQ ffA sigA clock clock_pol
if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) {
argQ = sigA;
subpattern(in_dffe);
@@ -72,20 +68,12 @@ code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
ffA = dff;
clock = dffclock;
clock_pol = dffclock_pol;
- if (dffrstmux) {
- ffArstmux = dffrstmux;
- ffArstpol = dffrstpol;
- }
- if (dffholdmux) {
- ffAholdmux = dffholdmux;
- ffAholdpol = dffholdpol;
- }
sigA = dffD;
}
}
endcode
-code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
+code argQ ffB sigB clock clock_pol
if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) {
argQ = sigB;
subpattern(in_dffe);
@@ -93,47 +81,44 @@ code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
ffB = dff;
clock = dffclock;
clock_pol = dffclock_pol;
- if (dffrstmux) {
- ffBrstmux = dffrstmux;
- ffBrstpol = dffrstpol;
- }
- if (dffholdmux) {
- ffBholdmux = dffholdmux;
- ffBholdpol = dffholdpol;
- }
sigB = dffD;
}
}
endcode
-code argD ffFJKG sigH clock clock_pol
+code argD argSdff ffFJKG sigH clock clock_pol
if (nusers(sigH) == 2 &&
(mul->type != \SB_MAC16 ||
(!param(mul, \TOP_8x8_MULT_REG).as_bool() && !param(mul, \BOT_8x8_MULT_REG).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool()))) {
argD = sigH;
+ argSdff = false;
subpattern(out_dffe);
if (dff) {
// F/J/K/G do not have a CE-like (hold) input
- if (dffholdmux)
+ if (dff->hasPort(\EN))
goto reject_ffFJKG;
// Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT)
// shared with A and B
- if ((ffArstmux != NULL) != (dffrstmux != NULL))
- goto reject_ffFJKG;
- if ((ffBrstmux != NULL) != (dffrstmux != NULL))
- goto reject_ffFJKG;
- if (ffArstmux) {
- if (port(ffArstmux, \S) != port(dffrstmux, \S))
- goto reject_ffFJKG;
- if (ffArstpol != dffrstpol)
+ if (ffA) {
+ if (ffA->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffFJKG;
+ if (ffA->hasPort(\ARST)) {
+ if (port(ffA, \ARST) != port(dff, \ARST))
+ goto reject_ffFJKG;
+ if (param(ffA, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
+ goto reject_ffFJKG;
+ }
}
- if (ffBrstmux) {
- if (port(ffBrstmux, \S) != port(dffrstmux, \S))
- goto reject_ffFJKG;
- if (ffBrstpol != dffrstpol)
+ if (ffB) {
+ if (ffB->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffFJKG;
+ if (ffB->hasPort(\ARST)) {
+ if (port(ffB, \ARST) != port(dff, \ARST))
+ goto reject_ffFJKG;
+ if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
+ goto reject_ffFJKG;
+ }
}
ffFJKG = dff;
@@ -146,23 +131,24 @@ reject_ffFJKG: ;
}
endcode
-code argD ffH sigH sigO clock clock_pol
+code argD argSdff ffH sigH sigO clock clock_pol
if (ffFJKG && nusers(sigH) == 2 &&
(mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) {
argD = sigH;
+ argSdff = false;
subpattern(out_dffe);
if (dff) {
// H does not have a CE-like (hold) input
- if (dffholdmux)
+ if (dff->hasPort(\EN))
goto reject_ffH;
// Reset signal of H (IRSTBOT) shared with B
- if ((ffBrstmux != NULL) != (dffrstmux != NULL))
+ if (ffB->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffH;
- if (ffBrstmux) {
- if (port(ffBrstmux, \S) != port(dffrstmux, \S))
+ if (ffB->hasPort(\ARST)) {
+ if (port(ffB, \ARST) != port(dff, \ARST))
goto reject_ffH;
- if (ffBrstpol != dffrstpol)
+ if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
goto reject_ffH;
}
@@ -226,7 +212,7 @@ code sigO
sigO = port(mux, \Y);
endcode
-code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_pol cd_signed o_lo
+code argD argSdff ffO sigO sigCD clock clock_pol cd_signed o_lo
if (mul->type != \SB_MAC16 ||
// Ensure that register is not already used
((param(mul, \TOPOUTPUT_SELECT).as_int() != 1 && param(mul, \BOTOUTPUT_SELECT).as_int() != 1) &&
@@ -238,6 +224,7 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p
// First try entire sigO
if (nusers(sigO) == 2) {
argD = sigO;
+ argSdff = !mux;
subpattern(out_dffe);
}
@@ -245,6 +232,7 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p
if (!dff && GetSize(sigO) > 16) {
argD = sigO.extract(0, 16);
if (nusers(argD) == 2) {
+ argSdff = !mux;
subpattern(out_dffe);
o_lo = dff;
}
@@ -254,14 +242,6 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p
ffO = dff;
clock = dffclock;
clock_pol = dffclock_pol;
- if (dffrstmux) {
- ffOrstmux = dffrstmux;
- ffOrstpol = dffrstpol;
- }
- if (dffholdmux) {
- ffOholdmux = dffholdmux;
- ffOholdpol = dffholdpol;
- }
sigO.replace(sigO.extract(0, GetSize(dffQ)), dffQ);
}
@@ -274,38 +254,43 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p
sigCD = port(mux, muxAB == \B ? \A : \B);
cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool();
+ } else if (dff && dff->hasPort(\SRST)) {
+ if (sigCD != sigO)
+ reject;
+ sigCD = param(dff, \SRST_VALUE);
+
+ cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool();
}
}
endcode
-code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol
+code argQ ffCD sigCD clock clock_pol
if (!sigCD.empty() && sigCD != sigO &&
(mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) {
argQ = sigCD;
subpattern(in_dffe);
if (dff) {
- if (dffholdmux) {
- ffCDholdmux = dffholdmux;
- ffCDholdpol = dffholdpol;
- }
-
// Reset signal of C (IRSTTOP) and D (IRSTBOT)
// shared with A and B
- if ((ffArstmux != NULL) != (dffrstmux != NULL))
- goto reject_ffCD;
- if ((ffBrstmux != NULL) != (dffrstmux != NULL))
- goto reject_ffCD;
- if (ffArstmux) {
- if (port(ffArstmux, \S) != port(dffrstmux, \S))
- goto reject_ffCD;
- if (ffArstpol != dffrstpol)
+ if (ffA) {
+ if (ffA->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffCD;
+ if (ffA->hasPort(\ARST)) {
+ if (port(ffA, \ARST) != port(dff, \ARST))
+ goto reject_ffCD;
+ if (param(ffA, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
+ goto reject_ffCD;
+ }
}
- if (ffBrstmux) {
- if (port(ffBrstmux, \S) != port(dffrstmux, \S))
- goto reject_ffCD;
- if (ffBrstpol != dffrstpol)
+ if (ffB) {
+ if (ffB->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffCD;
+ if (ffB->hasPort(\ARST)) {
+ if (port(ffB, \ARST) != port(dff, \ARST))
+ goto reject_ffCD;
+ if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
+ goto reject_ffCD;
+ }
}
ffCD = dff;
@@ -347,7 +332,7 @@ code
endcode
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
@@ -357,8 +342,6 @@ match ff
// Check that the rest of argQ is present
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
-
- set ffoffset offset
endmatch
code argQ argD
@@ -378,72 +361,13 @@ code argQ argD
argD = port(ff, \D);
argQ = Q;
dffD.replace(argQ, argD);
- // Only search for ffrstmux if dffD only
- // has two (ff, ffrstmux) users
- if (nusers(dffD) > 2)
- argD = SigSpec();
}
endcode
-match ffrstmux
- if false /* TODO: ice40 resets are actually async */
-
- if !argD.empty()
- select ffrstmux->type.in($mux)
- index <SigSpec> port(ffrstmux, \Y) === argD
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- define <bool> pol (BA == \B)
- set ffrstpol pol
- semioptional
-endmatch
-
-code argD
- if (ffrstmux) {
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- argD = port(ffrstmux, ffrstpol ? \A : \B);
- dffD.replace(port(ffrstmux, \Y), argD);
-
- // Only search for ffholdmux if argQ has at
- // least 3 users (ff, <upstream>, ffrstmux) and
- // dffD only has two (ff, ffrstmux)
- if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
- argD = SigSpec();
- }
- else
- dffrstmux = nullptr;
-endcode
-
-match ffholdmux
- if !argD.empty()
- select ffholdmux->type.in($mux)
- index <SigSpec> port(ffholdmux, \Y) === argD
- choice <IdString> BA {\B, \A}
- index <SigSpec> port(ffholdmux, BA) === argQ
- define <bool> pol (BA == \B)
- set ffholdpol pol
- semioptional
-endmatch
-
-code argD
- if (ffholdmux) {
- dffholdmux = ffholdmux;
- dffholdpol = ffholdpol;
- argD = port(ffholdmux, ffholdpol ? \A : \B);
- dffD.replace(port(ffholdmux, \Y), argD);
- }
- else
- dffholdmux = nullptr;
-endcode
-
// #######################
subpattern out_dffe
-arg argD argQ clock clock_pol
+arg argD argSdff argQ clock clock_pol
code
dff = nullptr;
@@ -452,101 +376,19 @@ code
reject;
endcode
-match ffholdmux
- select ffholdmux->type.in($mux)
- // ffholdmux output must have two users: ffholdmux and ff.D
- select nusers(port(ffholdmux, \Y)) == 2
-
- choice <IdString> BA {\B, \A}
- // keep-last-value net must have at least three users: ffholdmux, ff, downstream sink(s)
- select nusers(port(ffholdmux, BA)) >= 3
-
- slice offset GetSize(port(ffholdmux, \Y))
- define <IdString> AB (BA == \B ? \A : \B)
- index <SigBit> port(ffholdmux, AB)[offset] === argD[0]
-
- // Check that the rest of argD is present
- filter GetSize(port(ffholdmux, AB)) >= offset + GetSize(argD)
- filter port(ffholdmux, AB).extract(offset, GetSize(argD)) == argD
-
- set ffoffset offset
- define <bool> pol (BA == \B)
- set ffholdpol pol
-
- semioptional
-endmatch
-
-code argD argQ
- dffholdmux = ffholdmux;
- if (ffholdmux) {
- SigSpec AB = port(ffholdmux, ffholdpol ? \A : \B);
- SigSpec Y = port(ffholdmux, \Y);
- argQ = argD;
- argD.replace(AB, Y);
- argQ.replace(AB, port(ffholdmux, ffholdpol ? \B : \A));
-
- dffholdmux = ffholdmux;
- dffholdpol = ffholdpol;
- }
-endcode
-
-match ffrstmux
- if false /* TODO: ice40 resets are actually async */
-
- select ffrstmux->type.in($mux)
- // ffrstmux output must have two users: ffrstmux and ff.D
- select nusers(port(ffrstmux, \Y)) == 2
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- slice offset GetSize(port(ffrstmux, \Y))
- define <IdString> AB (BA == \B ? \A : \B)
- index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
-
- // Check that offset is consistent
- filter !ffholdmux || ffoffset == offset
- // Check that the rest of argD is present
- filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
- filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
-
- set ffoffset offset
- define <bool> pol (AB == \A)
- set ffrstpol pol
-
- semioptional
-endmatch
-
-code argD argQ
- dffrstmux = ffrstmux;
- if (ffrstmux) {
- SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
- SigSpec Y = port(ffrstmux, \Y);
- argD.replace(AB, Y);
-
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- }
-endcode
-
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe, $sdff, $sdffce)
// SB_MAC16 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \D)[offset] === argD[0]
- // Check that offset is consistent
- filter (!ffholdmux && !ffrstmux) || ffoffset == offset
+ // Only allow sync reset if requested.
+ filter argSdff || ff->type.in($dff, $dffe)
// Check that the rest of argD is present
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
- // Check that FF.Q is connected to CE-mux
- filter !ffholdmux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
-
- set ffoffset offset
endmatch
code argQ
@@ -559,10 +401,8 @@ code argQ
}
SigSpec D = port(ff, \D);
SigSpec Q = port(ff, \Q);
- if (!ffholdmux) {
- argQ = argD;
- argQ.replace(D, Q);
- }
+ argQ = argD;
+ argQ.replace(D, Q);
for (auto c : argQ.chunks()) {
Const init = c.wire->attributes.at(\init, State::Sx);
@@ -575,7 +415,4 @@ code argQ
dffclock = port(ff, \CLK);
dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
}
- // No enable/reset mux possible without flop
- else if (dffholdmux || dffrstmux)
- reject;
endcode
diff --git a/passes/pmgen/peepopt.cc b/passes/pmgen/peepopt.cc
index c16b4486d..a9c62fcf6 100644
--- a/passes/pmgen/peepopt.cc
+++ b/passes/pmgen/peepopt.cc
@@ -67,8 +67,6 @@ struct PeepoptPass : public Pass {
GENERATE_PATTERN(peepopt_pm, shiftmul);
else if (genmode == "muldiv")
GENERATE_PATTERN(peepopt_pm, muldiv);
- else if (genmode == "dffmux")
- GENERATE_PATTERN(peepopt_pm, dffmux);
else
log_abort();
return;
@@ -106,7 +104,6 @@ struct PeepoptPass : public Pass {
pm.run_shiftmul();
pm.run_muldiv();
- pm.run_dffmux();
for (auto w : module->wires()) {
auto it = w->attributes.find(ID::init);
diff --git a/passes/pmgen/peepopt_dffmux.pmg b/passes/pmgen/peepopt_dffmux.pmg
deleted file mode 100644
index 0069b0570..000000000
--- a/passes/pmgen/peepopt_dffmux.pmg
+++ /dev/null
@@ -1,171 +0,0 @@
-pattern dffmux
-
-state <IdString> cemuxAB rstmuxBA
-state <SigSpec> sigD
-
-match dff
- select dff->type == $dff
- select GetSize(port(dff, \D)) > 1
-endmatch
-
-code sigD
- sigD = port(dff, \D);
-endcode
-
-match rstmux
- select rstmux->type == $mux
- select GetSize(port(rstmux, \Y)) > 1
- index <SigSpec> port(rstmux, \Y) === sigD
- choice <IdString> BA {\B, \A}
- select port(rstmux, BA).is_fully_const()
- set rstmuxBA BA
- semioptional
-endmatch
-
-code sigD
- if (rstmux)
- sigD = port(rstmux, rstmuxBA == \B ? \A : \B);
-endcode
-
-match cemux
- select cemux->type == $mux
- select GetSize(port(cemux, \Y)) > 1
- index <SigSpec> port(cemux, \Y) === sigD
- choice <IdString> AB {\A, \B}
- index <SigSpec> port(cemux, AB) === port(dff, \Q)
- set cemuxAB AB
- semioptional
-endmatch
-
-code
- if (!cemux && !rstmux)
- reject;
-endcode
-
-code
- Const rst;
- SigSpec D;
- if (cemux) {
- D = port(cemux, cemuxAB == \A ? \B : \A);
- if (rstmux)
- rst = port(rstmux, rstmuxBA).as_const();
- else
- rst = Const(State::Sx, GetSize(D));
- }
- else {
- log_assert(rstmux);
- D = port(rstmux, rstmuxBA == \B ? \A : \B);
- rst = port(rstmux, rstmuxBA).as_const();
- }
- SigSpec Q = port(dff, \Q);
- int width = GetSize(D);
-
- SigSpec dffD = dff->getPort(\D);
- SigSpec dffQ = dff->getPort(\Q);
-
- Const initval;
- for (auto b : Q) {
- auto it = initbits.find(b);
- initval.bits.push_back(it == initbits.end() ? State::Sx : it->second);
- }
-
- auto cmpx = [=](State lhs, State rhs) {
- if (lhs == State::Sx || rhs == State::Sx)
- return true;
- return lhs == rhs;
- };
-
- int i = width-1;
- while (i > 1) {
- if (D[i] != D[i-1])
- break;
- if (!cmpx(rst[i], rst[i-1]))
- break;
- if (!cmpx(initval[i], initval[i-1]))
- break;
- if (!cmpx(rst[i], initval[i]))
- break;
- rminitbits.insert(Q[i]);
- module->connect(Q[i], Q[i-1]);
- i--;
- }
- if (i < width-1) {
- did_something = true;
- if (cemux) {
- SigSpec ceA = cemux->getPort(\A);
- SigSpec ceB = cemux->getPort(\B);
- SigSpec ceY = cemux->getPort(\Y);
- ceA.remove(i, width-1-i);
- ceB.remove(i, width-1-i);
- ceY.remove(i, width-1-i);
- cemux->setPort(\A, ceA);
- cemux->setPort(\B, ceB);
- cemux->setPort(\Y, ceY);
- cemux->fixup_parameters();
- blacklist(cemux);
- }
- if (rstmux) {
- SigSpec rstA = rstmux->getPort(\A);
- SigSpec rstB = rstmux->getPort(\B);
- SigSpec rstY = rstmux->getPort(\Y);
- rstA.remove(i, width-1-i);
- rstB.remove(i, width-1-i);
- rstY.remove(i, width-1-i);
- rstmux->setPort(\A, rstA);
- rstmux->setPort(\B, rstB);
- rstmux->setPort(\Y, rstY);
- rstmux->fixup_parameters();
- blacklist(rstmux);
- }
- dffD.remove(i, width-1-i);
- dffQ.remove(i, width-1-i);
- dff->setPort(\D, dffD);
- dff->setPort(\Q, dffQ);
- dff->fixup_parameters();
- blacklist(dff);
-
- log("dffcemux pattern in %s: dff=%s, cemux=%s, rstmux=%s; removed top %d bits.\n", log_id(module), log_id(dff), log_id(cemux, "n/a"), log_id(rstmux, "n/a"), width-1-i);
- width = i+1;
- }
- if (cemux) {
- SigSpec ceA = cemux->getPort(\A);
- SigSpec ceB = cemux->getPort(\B);
- SigSpec ceY = cemux->getPort(\Y);
-
- int count = 0;
- for (int i = width-1; i >= 0; i--) {
- if (D[i].wire)
- continue;
- if (cmpx(rst[i], D[i].data) && cmpx(initval[i], D[i].data)) {
- count++;
- rminitbits.insert(Q[i]);
- module->connect(Q[i], D[i]);
- ceA.remove(i);
- ceB.remove(i);
- ceY.remove(i);
- dffD.remove(i);
- dffQ.remove(i);
- }
- }
- if (count > 0)
- {
- did_something = true;
-
- cemux->setPort(\A, ceA);
- cemux->setPort(\B, ceB);
- cemux->setPort(\Y, ceY);
- cemux->fixup_parameters();
- blacklist(cemux);
-
- dff->setPort(\D, dffD);
- dff->setPort(\Q, dffQ);
- dff->fixup_parameters();
- blacklist(dff);
-
- log("dffcemux pattern in %s: dff=%s, cemux=%s, rstmux=%s; removed %d constant bits.\n", log_id(module), log_id(dff), log_id(cemux), log_id(rstmux, "n/a"), count);
- }
- }
-
- if (did_something)
- accept;
-endcode
diff --git a/passes/pmgen/peepopt_muldiv.pmg b/passes/pmgen/peepopt_muldiv.pmg
index 7cad759d0..a4e232342 100644
--- a/passes/pmgen/peepopt_muldiv.pmg
+++ b/passes/pmgen/peepopt_muldiv.pmg
@@ -1,16 +1,18 @@
pattern muldiv
state <SigSpec> t x y
+state <bool> is_signed
match mul
select mul->type == $mul
select GetSize(port(mul, \A)) + GetSize(port(mul, \B)) <= GetSize(port(mul, \Y))
endmatch
-code t x y
+code t x y is_signed
t = port(mul, \Y);
x = port(mul, \A);
y = port(mul, \B);
+ is_signed = param(mul, \A_SIGNED).as_bool();
branch;
std::swap(x, y);
endcode
@@ -19,6 +21,7 @@ match div
select div->type.in($div)
index <SigSpec> port(div, \A) === t
index <SigSpec> port(div, \B) === x
+ filter param(div, \A_SIGNED).as_bool() == is_signed
endmatch
code
diff --git a/passes/pmgen/peepopt_shiftmul.pmg b/passes/pmgen/peepopt_shiftmul.pmg
index d4748ae19..d71fbf744 100644
--- a/passes/pmgen/peepopt_shiftmul.pmg
+++ b/passes/pmgen/peepopt_shiftmul.pmg
@@ -31,22 +31,18 @@ match mul
select mul->type.in($mul)
select port(mul, \A).is_fully_const() || port(mul, \B).is_fully_const()
index <SigSpec> port(mul, \Y) === shamt
+ filter !param(mul, \A_SIGNED).as_bool()
endmatch
code
{
IdString const_factor_port = port(mul, \A).is_fully_const() ? \A : \B;
- IdString const_factor_signed = const_factor_port == \A ? \A_SIGNED : \B_SIGNED;
Const const_factor_cnst = port(mul, const_factor_port).as_const();
int const_factor = const_factor_cnst.as_int();
if (GetSize(const_factor_cnst) == 0)
reject;
- if (const_factor_cnst.bits[GetSize(const_factor_cnst)-1] != State::S0 &&
- param(mul, const_factor_signed).as_bool())
- reject;
-
if (GetSize(const_factor_cnst) > 20)
reject;
diff --git a/passes/pmgen/xilinx_dsp.cc b/passes/pmgen/xilinx_dsp.cc
index d05157270..cf7703d36 100644
--- a/passes/pmgen/xilinx_dsp.cc
+++ b/passes/pmgen/xilinx_dsp.cc
@@ -263,17 +263,17 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
log("Analysing %s.%s for Xilinx DSP packing.\n", log_id(pm.module), log_id(st.dsp));
log_debug("preAdd: %s\n", log_id(st.preAdd, "--"));
- log_debug("ffAD: %s %s %s\n", log_id(st.ffAD, "--"), log_id(st.ffADcemux, "--"), log_id(st.ffADrstmux, "--"));
- log_debug("ffA2: %s %s %s\n", log_id(st.ffA2, "--"), log_id(st.ffA2cemux, "--"), log_id(st.ffA2rstmux, "--"));
- log_debug("ffA1: %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--"));
- log_debug("ffB2: %s %s %s\n", log_id(st.ffB2, "--"), log_id(st.ffB2cemux, "--"), log_id(st.ffB2rstmux, "--"));
- log_debug("ffB1: %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--"));
- log_debug("ffD: %s %s %s\n", log_id(st.ffD, "--"), log_id(st.ffDcemux, "--"), log_id(st.ffDrstmux, "--"));
+ log_debug("ffAD: %s\n", log_id(st.ffAD, "--"));
+ log_debug("ffA2: %s\n", log_id(st.ffA2, "--"));
+ log_debug("ffA1: %s\n", log_id(st.ffA1, "--"));
+ log_debug("ffB2: %s\n", log_id(st.ffB2, "--"));
+ log_debug("ffB1: %s\n", log_id(st.ffB1, "--"));
+ log_debug("ffD: %s\n", log_id(st.ffD, "--"));
log_debug("dsp: %s\n", log_id(st.dsp, "--"));
- log_debug("ffM: %s %s %s\n", log_id(st.ffM, "--"), log_id(st.ffMcemux, "--"), log_id(st.ffMrstmux, "--"));
+ log_debug("ffM: %s\n", log_id(st.ffM, "--"));
log_debug("postAdd: %s\n", log_id(st.postAdd, "--"));
log_debug("postAddMux: %s\n", log_id(st.postAddMux, "--"));
- log_debug("ffP: %s %s %s\n", log_id(st.ffP, "--"), log_id(st.ffPcemux, "--"), log_id(st.ffPrstmux, "--"));
+ log_debug("ffP: %s\n", log_id(st.ffP, "--"));
log_debug("overflow: %s\n", log_id(st.overflow, "--"));
Cell *cell = st.dsp;
@@ -291,9 +291,10 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
cell->setPort(ID(INMODE), Const::from_string("00100"));
if (st.ffAD) {
- if (st.ffADcemux) {
- SigSpec S = st.ffADcemux->getPort(ID::S);
- cell->setPort(ID(CEAD), st.ffADcepol ? S : pm.module->Not(NEW_ID, S));
+ if (st.ffAD->type.in(ID($dffe), ID($sdffe))) {
+ bool pol = st.ffAD->getParam(ID::EN_POLARITY).as_bool();
+ SigSpec S = st.ffAD->getPort(ID::EN);
+ cell->setPort(ID(CEAD), pol ? S : pm.module->Not(NEW_ID, S));
}
else
cell->setPort(ID(CEAD), State::S1);
@@ -363,30 +364,24 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
{
cell->setPort(ID::CLK, st.clock);
- auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
+ auto f = [&pm,cell](SigSpec &A, Cell* ff, IdString ceport, IdString rstport) {
SigSpec D = ff->getPort(ID::D);
SigSpec Q = pm.sigmap(ff->getPort(ID::Q));
if (!A.empty())
A.replace(Q, D);
- if (rstmux) {
- SigSpec Y = rstmux->getPort(ID::Y);
- SigSpec AB = rstmux->getPort(rstpol ? ID::A : ID::B);
- if (!A.empty())
- A.replace(Y, AB);
- if (rstport != IdString()) {
- SigSpec S = rstmux->getPort(ID::S);
- cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
+ if (rstport != IdString()) {
+ if (ff->type.in(ID($sdff), ID($sdffe))) {
+ SigSpec srst = ff->getPort(ID::SRST);
+ bool rstpol = ff->getParam(ID::SRST_POLARITY).as_bool();
+ cell->setPort(rstport, rstpol ? srst : pm.module->Not(NEW_ID, srst));
+ } else {
+ cell->setPort(rstport, State::S0);
}
}
- else if (rstport != IdString())
- cell->setPort(rstport, State::S0);
- if (cemux) {
- SigSpec Y = cemux->getPort(ID::Y);
- SigSpec BA = cemux->getPort(cepol ? ID::B : ID::A);
- SigSpec S = cemux->getPort(ID::S);
- if (!A.empty())
- A.replace(Y, BA);
- cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
+ if (ff->type.in(ID($dffe), ID($sdffe))) {
+ SigSpec ce = ff->getPort(ID::EN);
+ bool cepol = ff->getParam(ID::EN_POLARITY).as_bool();
+ cell->setPort(ceport, cepol ? ce : pm.module->Not(NEW_ID, ce));
}
else
cell->setPort(ceport, State::S1);
@@ -404,9 +399,9 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
if (st.ffA2) {
SigSpec A = cell->getPort(ID::A);
- f(A, st.ffA2, st.ffA2cemux, st.ffA2cepol, ID(CEA2), st.ffA2rstmux, st.ffArstpol, ID(RSTA));
+ f(A, st.ffA2, ID(CEA2), ID(RSTA));
if (st.ffA1) {
- f(A, st.ffA1, st.ffA1cemux, st.ffA1cepol, ID(CEA1), st.ffA1rstmux, st.ffArstpol, IdString());
+ f(A, st.ffA1, ID(CEA1), IdString());
cell->setParam(ID(AREG), 2);
cell->setParam(ID(ACASCREG), 2);
}
@@ -419,9 +414,9 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
}
if (st.ffB2) {
SigSpec B = cell->getPort(ID::B);
- f(B, st.ffB2, st.ffB2cemux, st.ffB2cepol, ID(CEB2), st.ffB2rstmux, st.ffBrstpol, ID(RSTB));
+ f(B, st.ffB2, ID(CEB2), ID(RSTB));
if (st.ffB1) {
- f(B, st.ffB1, st.ffB1cemux, st.ffB1cepol, ID(CEB1), st.ffB1rstmux, st.ffBrstpol, IdString());
+ f(B, st.ffB1, ID(CEB1), IdString());
cell->setParam(ID(BREG), 2);
cell->setParam(ID(BCASCREG), 2);
}
@@ -434,20 +429,20 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
}
if (st.ffD) {
SigSpec D = cell->getPort(ID::D);
- f(D, st.ffD, st.ffDcemux, st.ffDcepol, ID(CED), st.ffDrstmux, st.ffDrstpol, ID(RSTD));
+ f(D, st.ffD, ID(CED), ID(RSTD));
pm.add_siguser(D, cell);
cell->setPort(ID::D, D);
cell->setParam(ID(DREG), 1);
}
if (st.ffM) {
SigSpec M; // unused
- f(M, st.ffM, st.ffMcemux, st.ffMcepol, ID(CEM), st.ffMrstmux, st.ffMrstpol, ID(RSTM));
+ f(M, st.ffM, ID(CEM), ID(RSTM));
st.ffM->connections_.at(ID::Q).replace(st.sigM, pm.module->addWire(NEW_ID, GetSize(st.sigM)));
cell->setParam(ID(MREG), State::S1);
}
if (st.ffP) {
SigSpec P; // unused
- f(P, st.ffP, st.ffPcemux, st.ffPcepol, ID(CEP), st.ffPrstmux, st.ffPrstpol, ID(RSTP));
+ f(P, st.ffP, ID(CEP), ID(RSTP));
st.ffP->connections_.at(ID::Q).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP)));
cell->setParam(ID(PREG), State::S1);
}
@@ -495,16 +490,16 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm)
log("Analysing %s.%s for Xilinx DSP48A/DSP48A1 packing.\n", log_id(pm.module), log_id(st.dsp));
log_debug("preAdd: %s\n", log_id(st.preAdd, "--"));
- log_debug("ffA1: %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--"));
- log_debug("ffA0: %s %s %s\n", log_id(st.ffA0, "--"), log_id(st.ffA0cemux, "--"), log_id(st.ffA0rstmux, "--"));
- log_debug("ffB1: %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--"));
- log_debug("ffB0: %s %s %s\n", log_id(st.ffB0, "--"), log_id(st.ffB0cemux, "--"), log_id(st.ffB0rstmux, "--"));
- log_debug("ffD: %s %s %s\n", log_id(st.ffD, "--"), log_id(st.ffDcemux, "--"), log_id(st.ffDrstmux, "--"));
+ log_debug("ffA1: %s\n", log_id(st.ffA1, "--"));
+ log_debug("ffA0: %s\n", log_id(st.ffA0, "--"));
+ log_debug("ffB1: %s\n", log_id(st.ffB1, "--"));
+ log_debug("ffB0: %s\n", log_id(st.ffB0, "--"));
+ log_debug("ffD: %s\n", log_id(st.ffD, "--"));
log_debug("dsp: %s\n", log_id(st.dsp, "--"));
- log_debug("ffM: %s %s %s\n", log_id(st.ffM, "--"), log_id(st.ffMcemux, "--"), log_id(st.ffMrstmux, "--"));
+ log_debug("ffM: %s\n", log_id(st.ffM, "--"));
log_debug("postAdd: %s\n", log_id(st.postAdd, "--"));
log_debug("postAddMux: %s\n", log_id(st.postAddMux, "--"));
- log_debug("ffP: %s %s %s\n", log_id(st.ffP, "--"), log_id(st.ffPcemux, "--"), log_id(st.ffPrstmux, "--"));
+ log_debug("ffP: %s\n", log_id(st.ffP, "--"));
Cell *cell = st.dsp;
SigSpec &opmode = cell->connections_.at(ID(OPMODE));
@@ -556,30 +551,24 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm)
{
cell->setPort(ID::CLK, st.clock);
- auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
+ auto f = [&pm,cell](SigSpec &A, Cell* ff, IdString ceport, IdString rstport) {
SigSpec D = ff->getPort(ID::D);
SigSpec Q = pm.sigmap(ff->getPort(ID::Q));
if (!A.empty())
A.replace(Q, D);
- if (rstmux) {
- SigSpec Y = rstmux->getPort(ID::Y);
- SigSpec AB = rstmux->getPort(rstpol ? ID::A : ID::B);
- if (!A.empty())
- A.replace(Y, AB);
- if (rstport != IdString()) {
- SigSpec S = rstmux->getPort(ID::S);
- cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
+ if (rstport != IdString()) {
+ if (ff->type.in(ID($sdff), ID($sdffe))) {
+ SigSpec srst = ff->getPort(ID::SRST);
+ bool rstpol = ff->getParam(ID::SRST_POLARITY).as_bool();
+ cell->setPort(rstport, rstpol ? srst : pm.module->Not(NEW_ID, srst));
+ } else {
+ cell->setPort(rstport, State::S0);
}
}
- else if (rstport != IdString())
- cell->setPort(rstport, State::S0);
- if (cemux) {
- SigSpec Y = cemux->getPort(ID::Y);
- SigSpec BA = cemux->getPort(cepol ? ID::B : ID::A);
- SigSpec S = cemux->getPort(ID::S);
- if (!A.empty())
- A.replace(Y, BA);
- cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
+ if (ff->type.in(ID($dffe), ID($sdffe))) {
+ SigSpec ce = ff->getPort(ID::EN);
+ bool cepol = ff->getParam(ID::EN_POLARITY).as_bool();
+ cell->setPort(ceport, cepol ? ce : pm.module->Not(NEW_ID, ce));
}
else
cell->setPort(ceport, State::S1);
@@ -598,11 +587,11 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm)
if (st.ffA0 || st.ffA1) {
SigSpec A = cell->getPort(ID::A);
if (st.ffA1) {
- f(A, st.ffA1, st.ffA1cemux, st.ffAcepol, ID(CEA), st.ffA1rstmux, st.ffArstpol, ID(RSTA));
+ f(A, st.ffA1, ID(CEA), ID(RSTA));
cell->setParam(ID(A1REG), 1);
}
if (st.ffA0) {
- f(A, st.ffA0, st.ffA0cemux, st.ffAcepol, ID(CEA), st.ffA0rstmux, st.ffArstpol, ID(RSTA));
+ f(A, st.ffA0, ID(CEA), ID(RSTA));
cell->setParam(ID(A0REG), 1);
}
pm.add_siguser(A, cell);
@@ -611,11 +600,11 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm)
if (st.ffB0 || st.ffB1) {
SigSpec B = cell->getPort(ID::B);
if (st.ffB1) {
- f(B, st.ffB1, st.ffB1cemux, st.ffBcepol, ID(CEB), st.ffB1rstmux, st.ffBrstpol, ID(RSTB));
+ f(B, st.ffB1, ID(CEB), ID(RSTB));
cell->setParam(ID(B1REG), 1);
}
if (st.ffB0) {
- f(B, st.ffB0, st.ffB0cemux, st.ffBcepol, ID(CEB), st.ffB0rstmux, st.ffBrstpol, ID(RSTB));
+ f(B, st.ffB0, ID(CEB), ID(RSTB));
cell->setParam(ID(B0REG), 1);
}
pm.add_siguser(B, cell);
@@ -623,20 +612,20 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm)
}
if (st.ffD) {
SigSpec D = cell->getPort(ID::D);
- f(D, st.ffD, st.ffDcemux, st.ffDcepol, ID(CED), st.ffDrstmux, st.ffDrstpol, ID(RSTD));
+ f(D, st.ffD, ID(CED), ID(RSTD));
pm.add_siguser(D, cell);
cell->setPort(ID::D, D);
cell->setParam(ID(DREG), 1);
}
if (st.ffM) {
SigSpec M; // unused
- f(M, st.ffM, st.ffMcemux, st.ffMcepol, ID(CEM), st.ffMrstmux, st.ffMrstpol, ID(RSTM));
+ f(M, st.ffM, ID(CEM), ID(RSTM));
st.ffM->connections_.at(ID::Q).replace(st.sigM, pm.module->addWire(NEW_ID, GetSize(st.sigM)));
cell->setParam(ID(MREG), State::S1);
}
if (st.ffP) {
SigSpec P; // unused
- f(P, st.ffP, st.ffPcemux, st.ffPcepol, ID(CEP), st.ffPrstmux, st.ffPrstpol, ID(RSTP));
+ f(P, st.ffP, ID(CEP), ID(RSTP));
st.ffP->connections_.at(ID::Q).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP)));
cell->setParam(ID(PREG), State::S1);
}
@@ -677,7 +666,7 @@ void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm)
auto &st = pm.st_xilinx_dsp_packC;
log_debug("Analysing %s.%s for Xilinx DSP packing (CREG).\n", log_id(pm.module), log_id(st.dsp));
- log_debug("ffC: %s %s %s\n", log_id(st.ffC, "--"), log_id(st.ffCcemux, "--"), log_id(st.ffCrstmux, "--"));
+ log_debug("ffC: %s\n", log_id(st.ffC, "--"));
Cell *cell = st.dsp;
@@ -685,30 +674,24 @@ void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm)
{
cell->setPort(ID::CLK, st.clock);
- auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
+ auto f = [&pm,cell](SigSpec &A, Cell* ff, IdString ceport, IdString rstport) {
SigSpec D = ff->getPort(ID::D);
SigSpec Q = pm.sigmap(ff->getPort(ID::Q));
if (!A.empty())
A.replace(Q, D);
- if (rstmux) {
- SigSpec Y = rstmux->getPort(ID::Y);
- SigSpec AB = rstmux->getPort(rstpol ? ID::A : ID::B);
- if (!A.empty())
- A.replace(Y, AB);
- if (rstport != IdString()) {
- SigSpec S = rstmux->getPort(ID::S);
- cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
+ if (rstport != IdString()) {
+ if (ff->type.in(ID($sdff), ID($sdffe))) {
+ SigSpec srst = ff->getPort(ID::SRST);
+ bool rstpol = ff->getParam(ID::SRST_POLARITY).as_bool();
+ cell->setPort(rstport, rstpol ? srst : pm.module->Not(NEW_ID, srst));
+ } else {
+ cell->setPort(rstport, State::S0);
}
}
- else if (rstport != IdString())
- cell->setPort(rstport, State::S0);
- if (cemux) {
- SigSpec Y = cemux->getPort(ID::Y);
- SigSpec BA = cemux->getPort(cepol ? ID::B : ID::A);
- SigSpec S = cemux->getPort(ID::S);
- if (!A.empty())
- A.replace(Y, BA);
- cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
+ if (ff->type.in(ID($dffe), ID($sdffe))) {
+ SigSpec ce = ff->getPort(ID::EN);
+ bool cepol = ff->getParam(ID::EN_POLARITY).as_bool();
+ cell->setPort(ceport, cepol ? ce : pm.module->Not(NEW_ID, ce));
}
else
cell->setPort(ceport, State::S1);
@@ -726,7 +709,7 @@ void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm)
if (st.ffC) {
SigSpec C = cell->getPort(ID::C);
- f(C, st.ffC, st.ffCcemux, st.ffCcepol, ID(CEC), st.ffCrstmux, st.ffCrstpol, ID(RSTC));
+ f(C, st.ffC, ID(CEC), ID(RSTC));
pm.add_siguser(C, cell);
cell->setPort(ID::C, C);
cell->setParam(ID(CREG), 1);
diff --git a/passes/pmgen/xilinx_dsp.pmg b/passes/pmgen/xilinx_dsp.pmg
index d40f073c9..0cd23c09d 100644
--- a/passes/pmgen/xilinx_dsp.pmg
+++ b/passes/pmgen/xilinx_dsp.pmg
@@ -2,9 +2,7 @@
// forms the `xilinx_dsp` pass described in xilinx_dsp.cc
// At a high level, it works as follows:
// ( 1) Starting from a DSP48E1 cell
-// ( 2) Match the driver of the 'A' input to a possible $dff cell (ADREG)
-// (attached to at most two $mux cells that implement clock-enable or
-// reset functionality, using a subpattern discussed below)
+// ( 2) Match the driver of the 'A' input to a possible $sdffe cell (ADREG)
// If ADREG matched, treat 'A' input as input of ADREG
// ( 3) Match the driver of the 'A' and 'D' inputs for a possible $add cell
// (pre-adder)
@@ -44,7 +42,7 @@
// DSP48E1 cells inferred from multiply operations by Yosys, as well as for
// user instantiations that may already contain the cells being packed...
// (though the latter is currently untested)
-// - Since the $dff-with-optional-clock-enable-or-reset-mux pattern is used
+// - Since the $sdffe pattern is used
// for each *REG match, it has been factored out into two subpatterns:
// in_dffe and out_dffe located at the bottom of this file.
// - Matching for pattern detector features is currently incomplete. For
@@ -57,20 +55,15 @@ pattern xilinx_dsp_pack
state <SigBit> clock
state <SigSpec> sigA sigB sigC sigD sigM sigP
state <IdString> postAddAB postAddMuxAB
-state <bool> ffA1cepol ffA2cepol ffADcepol ffB1cepol ffB2cepol ffDcepol ffMcepol ffPcepol
-state <bool> ffArstpol ffADrstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol
-state <Cell*> ffAD ffADcemux ffADrstmux ffA1 ffA1cemux ffA1rstmux ffA2 ffA2cemux ffA2rstmux
-state <Cell*> ffB1 ffB1cemux ffB1rstmux ffB2 ffB2cemux ffB2rstmux
-state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux
+state <Cell*> ffAD ffA1 ffA2
+state <Cell*> ffB1 ffB2
+state <Cell*> ffD ffM ffP
// Variables used for subpatterns
state <SigSpec> argQ argD
-state <bool> ffcepol ffrstpol
-state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
-udata <Cell*> dff dffcemux dffrstmux
-udata <bool> dffcepol dffrstpol
+udata <Cell*> dff
// (1) Starting from a DSP48E1 cell
match dsp
@@ -115,25 +108,15 @@ code sigA sigB sigC sigD sigM clock
clock = port(dsp, \CLK, SigBit());
endcode
-// (2) Match the driver of the 'A' input to a possible $dff cell (ADREG)
-// (attached to at most two $mux cells that implement clock-enable or
-// reset functionality, using a subpattern discussed above)
+// (2) Match the driver of the 'A' input to a possible $sdffe cell (ADREG)
// If matched, treat 'A' input as input of ADREG
-code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock
+code argQ ffAD sigA clock
if (param(dsp, \ADREG).as_int() == 0) {
argQ = sigA;
subpattern(in_dffe);
if (dff) {
ffAD = dff;
clock = dffclock;
- if (dffrstmux) {
- ffADrstmux = dffrstmux;
- ffADrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffADcemux = dffcemux;
- ffADcepol = dffcepol;
- }
sigA = dffD;
}
}
@@ -172,7 +155,7 @@ endcode
// (4) If pre-adder was present, find match 'A' input for A2REG
// If pre-adder was not present, move ADREG to A2REG
// Then match 'A' input for A1REG
-code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cemux ffA2rstmux ffA2cepol ffArstpol ffA1 ffA1cemux ffA1rstmux ffA1cepol
+code argQ ffAD sigA clock ffA2 ffA1
// Only search for ffA2 if there was a pre-adder
// (otherwise ffA2 would have been matched as ffAD)
if (preAdd) {
@@ -182,14 +165,6 @@ code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cem
if (dff) {
ffA2 = dff;
clock = dffclock;
- if (dffrstmux) {
- ffA2rstmux = dffrstmux;
- ffArstpol = dffrstpol;
- }
- if (dffcemux) {
- ffA2cepol = dffcepol;
- ffA2cemux = dffcemux;
- }
sigA = dffD;
}
}
@@ -197,12 +172,8 @@ code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cem
// And if there wasn't a pre-adder,
// move AD register to A
else if (ffAD) {
- log_assert(!ffA2 && !ffA2cemux && !ffA2rstmux);
+ log_assert(!ffA2);
std::swap(ffA2, ffAD);
- std::swap(ffA2cemux, ffADcemux);
- std::swap(ffA2rstmux, ffADrstmux);
- ffA2cepol = ffADcepol;
- ffArstpol = ffADrstpol;
}
// Now attempt to match A1
@@ -210,23 +181,23 @@ code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cem
argQ = sigA;
subpattern(in_dffe);
if (dff) {
- if ((ffA2rstmux != nullptr) ^ (dffrstmux != nullptr))
+ if (dff->type != ffA2->type)
goto ffA1_end;
- if (dffrstmux) {
- if (ffArstpol != dffrstpol)
+ if (dff->type.in($sdff, $sdffe, $sdffce)) {
+ if (param(dff, \SRST_POLARITY) != param(ffA2, \SRST_POLARITY))
goto ffA1_end;
- if (port(ffA2rstmux, \S) != port(dffrstmux, \S))
+ if (port(dff, \SRST) != port(ffA2, \SRST))
+ goto ffA1_end;
+ }
+ if (dff->type.in($dffe, $sdffe, $sdffce)) {
+ if (param(dff, \EN_POLARITY) != param(ffA2, \EN_POLARITY))
+ goto ffA1_end;
+ if (port(dff, \EN) != port(ffA2, \EN))
goto ffA1_end;
- ffA1rstmux = dffrstmux;
}
ffA1 = dff;
clock = dffclock;
-
- if (dffcemux) {
- ffA1cemux = dffcemux;
- ffA1cepol = dffcepol;
- }
sigA = dffD;
ffA1_end: ;
@@ -236,21 +207,13 @@ endcode
// (5) Match 'B' input for B2REG
// If B2REG, then match 'B' input for B1REG
-code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemux ffB1rstmux ffB1cepol
+code argQ ffB2 sigB clock ffB1
if (param(dsp, \BREG).as_int() == 0) {
argQ = sigB;
subpattern(in_dffe);
if (dff) {
ffB2 = dff;
clock = dffclock;
- if (dffrstmux) {
- ffB2rstmux = dffrstmux;
- ffBrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffB2cemux = dffcemux;
- ffB2cepol = dffcepol;
- }
sigB = dffD;
// Now attempt to match B1
@@ -258,23 +221,23 @@ code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemu
argQ = sigB;
subpattern(in_dffe);
if (dff) {
- if ((ffB2rstmux != nullptr) ^ (dffrstmux != nullptr))
+ if (dff->type != ffB2->type)
goto ffB1_end;
- if (dffrstmux) {
- if (ffBrstpol != dffrstpol)
+ if (dff->type.in($sdff, $sdffe, $sdffce)) {
+ if (param(dff, \SRST_POLARITY) != param(ffB2, \SRST_POLARITY))
+ goto ffB1_end;
+ if (port(dff, \SRST) != port(ffB2, \SRST))
+ goto ffB1_end;
+ }
+ if (dff->type.in($dffe, $sdffe, $sdffce)) {
+ if (param(dff, \EN_POLARITY) != param(ffB2, \EN_POLARITY))
goto ffB1_end;
- if (port(ffB2rstmux, \S) != port(dffrstmux, \S))
+ if (port(dff, \EN) != port(ffB2, \EN))
goto ffB1_end;
- ffB1rstmux = dffrstmux;
}
ffB1 = dff;
clock = dffclock;
-
- if (dffcemux) {
- ffB1cemux = dffcemux;
- ffB1cepol = dffcepol;
- }
sigB = dffD;
ffB1_end: ;
@@ -286,42 +249,26 @@ ffB1_end: ;
endcode
// (6) Match 'D' input for DREG
-code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
+code argQ ffD sigD clock
if (param(dsp, \DREG).as_int() == 0) {
argQ = sigD;
subpattern(in_dffe);
if (dff) {
ffD = dff;
clock = dffclock;
- if (dffrstmux) {
- ffDrstmux = dffrstmux;
- ffDrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffDcemux = dffcemux;
- ffDcepol = dffcepol;
- }
sigD = dffD;
}
}
endcode
// (7) Match 'P' output that exclusively drives an MREG
-code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
+code argD ffM sigM sigP clock
if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) {
argD = sigM;
subpattern(out_dffe);
if (dff) {
ffM = dff;
clock = dffclock;
- if (dffrstmux) {
- ffMrstmux = dffrstmux;
- ffMrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffMcemux = dffcemux;
- ffMcepol = dffcepol;
- }
sigM = dffQ;
}
}
@@ -340,9 +287,7 @@ match postAdd
select postAdd->type.in($add)
select GetSize(port(postAdd, \Y)) <= 48
choice <IdString> AB {\A, \B}
- select nusers(port(postAdd, AB)) <= 3
- filter ffMcemux || nusers(port(postAdd, AB)) == 2
- filter !ffMcemux || nusers(port(postAdd, AB)) == 3
+ select nusers(port(postAdd, AB)) == 2
index <SigBit> port(postAdd, AB)[0] === sigP[0]
filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
@@ -362,25 +307,14 @@ code sigC sigP
endcode
// (9) Match 'P' output that exclusively drives a PREG
-code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
+code argD ffP sigP clock
if (param(dsp, \PREG).as_int() == 0) {
- int users = 2;
- // If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
- if (ffMcemux && !postAdd) users++;
- if (nusers(sigP) == users) {
+ if (nusers(sigP) == 2) {
argD = sigP;
subpattern(out_dffe);
if (dff) {
ffP = dff;
clock = dffclock;
- if (dffrstmux) {
- ffPrstmux = dffrstmux;
- ffPrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffPcemux = dffcemux;
- ffPcepol = dffcepol;
- }
sigP = dffQ;
}
}
@@ -441,22 +375,9 @@ endcode
// #######################
// Subpattern for matching against input registers, based on knowledge of the
-// 'Q' input. Typically, identifying registers with clock-enable and reset
-// capability would be a task would be handled by other Yosys passes such as
-// dff2dffe, but since DSP inference happens much before this, these patterns
-// have to be manually identified.
-// At a high level:
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// one that exclusively drives the 'D' input of the $dff, with one of its
-// $mux inputs being fully zero
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
+// 'Q' input.
subpattern in_dffe
-arg argD argQ clock
+arg argQ clock
code
dff = nullptr;
@@ -479,13 +400,14 @@ code
}
endcode
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe, $sdff, $sdffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
+ // Check that reset value, if present, is fully 0.
+ filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero()
+
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
@@ -494,82 +416,16 @@ match ff
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock
-
- set ffoffset offset
endmatch
-code argQ argD
+code argQ
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffD = argQ;
- argD = port(ff, \D);
+ SigSpec D = port(ff, \D);
argQ = Q;
- dffD.replace(argQ, argD);
- // Only search for ffrstmux if dffD only
- // has two (ff, ffrstmux) users
- if (nusers(dffD) > 2)
- argD = SigSpec();
-endcode
-
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// exclusively drives the 'D' input of the $dff, with one of the $mux
-// inputs being fully zero
-match ffrstmux
- if !argD.empty()
- select ffrstmux->type.in($mux)
- index <SigSpec> port(ffrstmux, \Y) === argD
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- define <bool> pol (BA == \B)
- set ffrstpol pol
- semioptional
-endmatch
-
-code argD
- if (ffrstmux) {
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- argD = port(ffrstmux, ffrstpol ? \A : \B);
- dffD.replace(port(ffrstmux, \Y), argD);
-
- // Only search for ffcemux if argQ has at
- // least 3 users (ff, <upstream>, ffrstmux) and
- // dffD only has two (ff, ffrstmux)
- if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
- argD = SigSpec();
- }
- else
- dffrstmux = nullptr;
-endcode
-
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
-match ffcemux
- if !argD.empty()
- select ffcemux->type.in($mux)
- index <SigSpec> port(ffcemux, \Y) === argD
- choice <IdString> AB {\A, \B}
- index <SigSpec> port(ffcemux, AB) === argQ
- define <bool> pol (AB == \A)
- set ffcepol pol
- semioptional
-endmatch
-
-code argD
- if (ffcemux) {
- dffcemux = ffcemux;
- dffcepol = ffcepol;
- argD = port(ffcemux, ffcepol ? \B : \A);
- dffD.replace(port(ffcemux, \Y), argD);
- }
- else
- dffcemux = nullptr;
+ dffD.replace(argQ, D);
endcode
// #######################
@@ -597,119 +453,26 @@ code
reject;
endcode
-// (1) Starting from an optional $mux cell that implements clock enable
-// semantics --- one where the given 'D' argument (partially or fully)
-// drives one of its two inputs
-match ffcemux
- select ffcemux->type.in($mux)
- // ffcemux output must have two users: ffcemux and ff.D
- select nusers(port(ffcemux, \Y)) == 2
-
- choice <IdString> AB {\A, \B}
- // keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s)
- select nusers(port(ffcemux, AB)) >= 3
-
- slice offset GetSize(port(ffcemux, \Y))
- define <IdString> BA (AB == \A ? \B : \A)
- index <SigBit> port(ffcemux, BA)[offset] === argD[0]
-
- // Check that the rest of argD is present
- filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD)
- filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD
-
- set ffoffset offset
- define <bool> pol (AB == \A)
- set ffcepol pol
-
- semioptional
-endmatch
-
-code argD argQ
- dffcemux = ffcemux;
- if (ffcemux) {
- SigSpec BA = port(ffcemux, ffcepol ? \B : \A);
- SigSpec Y = port(ffcemux, \Y);
- argQ = argD;
- argD.replace(BA, Y);
- argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B));
-
- dffcemux = ffcemux;
- dffcepol = ffcepol;
- }
-endcode
-
-// (2) Starting from, or continuing onto, another optional $mux cell that
-// implements synchronous reset semantics --- one where the given 'D'
-// argument (or the clock enable $mux output) drives one of its two inputs
-// and where the other input is fully zero
-match ffrstmux
- select ffrstmux->type.in($mux)
- // ffrstmux output must have two users: ffrstmux and ff.D
- select nusers(port(ffrstmux, \Y)) == 2
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- slice offset GetSize(port(ffrstmux, \Y))
- define <IdString> AB (BA == \B ? \A : \B)
- index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
-
- // Check that offset is consistent
- filter !ffcemux || ffoffset == offset
- // Check that the rest of argD is present
- filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
- filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
-
- set ffoffset offset
- define <bool> pol (AB == \A)
- set ffrstpol pol
-
- semioptional
-endmatch
-
-code argD argQ
- dffrstmux = ffrstmux;
- if (ffrstmux) {
- SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
- SigSpec Y = port(ffrstmux, \Y);
- argD.replace(AB, Y);
-
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- }
-endcode
-
-// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
-// output of the previous clock enable or reset $mux cells)
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe, $sdff, $sdffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \D)[offset] === argD[0]
- // Check that offset is consistent
- filter (!ffcemux && !ffrstmux) || ffoffset == offset
// Check that the rest of argD is present
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
- // Check that FF.Q is connected to CE-mux
- filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock
-
- set ffoffset offset
endmatch
code argQ
SigSpec D = port(ff, \D);
SigSpec Q = port(ff, \Q);
- if (!ffcemux) {
- argQ = argD;
- argQ.replace(D, Q);
- }
+ argQ = argD;
+ argQ.replace(D, Q);
// Abandon matches when 'Q' has a non-zero init attribute set
// (not supported by DSP48E1)
diff --git a/passes/pmgen/xilinx_dsp48a.pmg b/passes/pmgen/xilinx_dsp48a.pmg
index 16f5e598d..dce1b61b0 100644
--- a/passes/pmgen/xilinx_dsp48a.pmg
+++ b/passes/pmgen/xilinx_dsp48a.pmg
@@ -4,8 +4,6 @@
// At a high level, it works as follows:
// ( 1) Starting from a DSP48A/DSP48A1 cell
// ( 2) Match the driver of the 'B' input to a possible $dff cell (B1REG)
-// (attached to at most two $mux cells that implement clock-enable or
-// reset functionality, using a subpattern discussed below)
// If B1REG matched, treat 'B' input as input of B1REG
// ( 3) Match the driver of the 'B' and 'D' inputs for a possible $add cell
// (pre-adder)
@@ -40,20 +38,15 @@ pattern xilinx_dsp48a_pack
state <SigBit> clock
state <SigSpec> sigA sigB sigC sigD sigM sigP
state <IdString> postAddAB postAddMuxAB
-state <bool> ffAcepol ffBcepol ffDcepol ffMcepol ffPcepol
-state <bool> ffArstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol
-state <Cell*> ffA0 ffA0cemux ffA0rstmux ffA1 ffA1cemux ffA1rstmux
-state <Cell*> ffB0 ffB0cemux ffB0rstmux ffB1 ffB1cemux ffB1rstmux
-state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux
+state <Cell*> ffA0 ffA1
+state <Cell*> ffB0 ffB1
+state <Cell*> ffD ffM ffP
// Variables used for subpatterns
state <SigSpec> argQ argD
-state <bool> ffcepol ffrstpol
-state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
-udata <Cell*> dff dffcemux dffrstmux
-udata <bool> dffcepol dffrstpol
+udata <Cell*> dff
// (1) Starting from a DSP48A/DSP48A1 cell
match dsp
@@ -98,21 +91,13 @@ endcode
// (attached to at most two $mux cells that implement clock-enable or
// reset functionality, using a subpattern discussed above)
// If matched, treat 'B' input as input of B1REG
-code argQ ffB1 ffB1cemux ffB1rstmux ffBcepol ffBrstpol sigB clock
+code argQ ffB1 sigB clock
if (param(dsp, \B1REG).as_int() == 0 && param(dsp, \B0REG).as_int() == 0 && port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()) {
argQ = sigB;
subpattern(in_dffe);
if (dff) {
ffB1 = dff;
clock = dffclock;
- if (dffrstmux) {
- ffB1rstmux = dffrstmux;
- ffBrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffB1cemux = dffcemux;
- ffBcepol = dffcepol;
- }
sigB = dffD;
}
}
@@ -147,41 +132,29 @@ code sigB sigD
endcode
// (4) Match 'B' input for B0REG
-code argQ ffB0 ffB0cemux ffB0rstmux ffBcepol ffBrstpol sigB clock
+code argQ ffB0 sigB clock
if (param(dsp, \B0REG).as_int() == 0) {
argQ = sigB;
subpattern(in_dffe);
if (dff) {
if (ffB1) {
- if ((ffB1rstmux != nullptr) ^ (dffrstmux != nullptr))
+ if (dff->type != ffB1->type)
goto ffB0_end;
- if ((ffB1cemux != nullptr) ^ (dffcemux != nullptr))
- goto ffB0_end;
- if (dffrstmux) {
- if (ffBrstpol != dffrstpol)
+ if (dff->type.in($sdff, $sdffe, $sdffce)) {
+ if (param(dff, \SRST_POLARITY) != param(ffB1, \SRST_POLARITY))
goto ffB0_end;
- if (port(ffB1rstmux, \S) != port(dffrstmux, \S))
+ if (port(dff, \SRST) != port(ffB1, \SRST))
goto ffB0_end;
- ffB0rstmux = dffrstmux;
}
- if (dffcemux) {
- if (ffBcepol != dffcepol)
+ if (dff->type.in($dffe, $sdffe, $sdffce)) {
+ if (param(dff, \EN_POLARITY) != param(ffB1, \EN_POLARITY))
goto ffB0_end;
- if (port(ffB1cemux, \S) != port(dffcemux, \S))
+ if (port(dff, \EN) != port(ffB1, \EN))
goto ffB0_end;
- ffB0cemux = dffcemux;
}
}
ffB0 = dff;
clock = dffclock;
- if (dffrstmux) {
- ffB0rstmux = dffrstmux;
- ffBrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffB0cemux = dffcemux;
- ffBcepol = dffcepol;
- }
sigB = dffD;
}
}
@@ -190,21 +163,13 @@ endcode
// (5) Match 'A' input for A1REG
// If A1REG, then match 'A' input for A0REG
-code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux ffA0rstmux
+code argQ ffA1 sigA clock ffA0
if (param(dsp, \A0REG).as_int() == 0 && param(dsp, \A1REG).as_int() == 0) {
argQ = sigA;
subpattern(in_dffe);
if (dff) {
ffA1 = dff;
clock = dffclock;
- if (dffrstmux) {
- ffA1rstmux = dffrstmux;
- ffArstpol = dffrstpol;
- }
- if (dffcemux) {
- ffA1cemux = dffcemux;
- ffAcepol = dffcepol;
- }
sigA = dffD;
// Now attempt to match A0
@@ -212,32 +177,23 @@ code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux
argQ = sigA;
subpattern(in_dffe);
if (dff) {
- if ((ffA1rstmux != nullptr) ^ (dffrstmux != nullptr))
+ if (dff->type != ffA1->type)
goto ffA0_end;
- if ((ffA1cemux != nullptr) ^ (dffcemux != nullptr))
- goto ffA0_end;
- if (dffrstmux) {
- if (ffArstpol != dffrstpol)
+ if (dff->type.in($sdff, $sdffe, $sdffce)) {
+ if (param(dff, \SRST_POLARITY) != param(ffA1, \SRST_POLARITY))
goto ffA0_end;
- if (port(ffA1rstmux, \S) != port(dffrstmux, \S))
+ if (port(dff, \SRST) != port(ffA1, \SRST))
goto ffA0_end;
- ffA0rstmux = dffrstmux;
}
- if (dffcemux) {
- if (ffAcepol != dffcepol)
+ if (dff->type.in($dffe, $sdffe, $sdffce)) {
+ if (param(dff, \EN_POLARITY) != param(ffA1, \EN_POLARITY))
goto ffA0_end;
- if (port(ffA1cemux, \S) != port(dffcemux, \S))
+ if (port(dff, \EN) != port(ffA1, \EN))
goto ffA0_end;
- ffA0cemux = dffcemux;
}
ffA0 = dff;
clock = dffclock;
-
- if (dffcemux) {
- ffA0cemux = dffcemux;
- ffAcepol = dffcepol;
- }
sigA = dffD;
ffA0_end: ;
@@ -249,42 +205,26 @@ ffA0_end: ;
endcode
// (6) Match 'D' input for DREG
-code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
+code argQ ffD sigD clock
if (param(dsp, \DREG).as_int() == 0) {
argQ = sigD;
subpattern(in_dffe);
if (dff) {
ffD = dff;
clock = dffclock;
- if (dffrstmux) {
- ffDrstmux = dffrstmux;
- ffDrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffDcemux = dffcemux;
- ffDcepol = dffcepol;
- }
sigD = dffD;
}
}
endcode
// (7) Match 'P' output that exclusively drives an MREG
-code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
+code argD ffM sigM sigP clock
if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) {
argD = sigM;
subpattern(out_dffe);
if (dff) {
ffM = dff;
clock = dffclock;
- if (dffrstmux) {
- ffMrstmux = dffrstmux;
- ffMrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffMcemux = dffcemux;
- ffMcepol = dffcepol;
- }
sigM = dffQ;
}
}
@@ -303,9 +243,7 @@ match postAdd
select postAdd->type.in($add)
select GetSize(port(postAdd, \Y)) <= 48
choice <IdString> AB {\A, \B}
- select nusers(port(postAdd, AB)) <= 3
- filter ffMcemux || nusers(port(postAdd, AB)) == 2
- filter !ffMcemux || nusers(port(postAdd, AB)) == 3
+ select nusers(port(postAdd, AB)) == 2
index <SigBit> port(postAdd, AB)[0] === sigP[0]
filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
@@ -325,25 +263,14 @@ code sigC sigP
endcode
// (9) Match 'P' output that exclusively drives a PREG
-code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
+code argD ffP sigP clock
if (param(dsp, \PREG).as_int() == 0) {
- int users = 2;
- // If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
- if (ffMcemux && !postAdd) users++;
- if (nusers(sigP) == users) {
+ if (nusers(sigP) == 2) {
argD = sigP;
subpattern(out_dffe);
if (dff) {
ffP = dff;
clock = dffclock;
- if (dffrstmux) {
- ffPrstmux = dffrstmux;
- ffPrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffPcemux = dffcemux;
- ffPcepol = dffcepol;
- }
sigP = dffQ;
}
}
@@ -387,26 +314,13 @@ endcode
// #######################
// Subpattern for matching against input registers, based on knowledge of the
-// 'Q' input. Typically, identifying registers with clock-enable and reset
-// capability would be a task would be handled by other Yosys passes such as
-// dff2dffe, but since DSP inference happens much before this, these patterns
-// have to be manually identified.
-// At a high level:
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// one that exclusively drives the 'D' input of the $dff, with one of its
-// $mux inputs being fully zero
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
+// 'Q' input.
subpattern in_dffe
-arg argD argQ clock
+arg argQ clock
code
dff = nullptr;
- if (GetSize(argQ) == 0)
+ if (argQ.empty())
reject;
for (const auto &c : argQ.chunks()) {
// Abandon matches when 'Q' is a constant
@@ -425,13 +339,14 @@ code
}
endcode
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe, $sdff, $sdffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
+ // Check that reset value, if present, is fully 0.
+ filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero()
+
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
@@ -440,82 +355,16 @@ match ff
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock
-
- set ffoffset offset
endmatch
-code argQ argD
+code argQ
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffD = argQ;
- argD = port(ff, \D);
+ SigSpec D = port(ff, \D);
argQ = Q;
- dffD.replace(argQ, argD);
- // Only search for ffrstmux if dffD only
- // has two (ff, ffrstmux) users
- if (nusers(dffD) > 2)
- argD = SigSpec();
-endcode
-
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// exclusively drives the 'D' input of the $dff, with one of the $mux
-// inputs being fully zero
-match ffrstmux
- if !argD.empty()
- select ffrstmux->type.in($mux)
- index <SigSpec> port(ffrstmux, \Y) === argD
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- define <bool> pol (BA == \B)
- set ffrstpol pol
- semioptional
-endmatch
-
-code argD
- if (ffrstmux) {
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- argD = port(ffrstmux, ffrstpol ? \A : \B);
- dffD.replace(port(ffrstmux, \Y), argD);
-
- // Only search for ffcemux if argQ has at
- // least 3 users (ff, <upstream>, ffrstmux) and
- // dffD only has two (ff, ffrstmux)
- if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
- argD = SigSpec();
- }
- else
- dffrstmux = nullptr;
-endcode
-
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
-match ffcemux
- if !argD.empty()
- select ffcemux->type.in($mux)
- index <SigSpec> port(ffcemux, \Y) === argD
- choice <IdString> AB {\A, \B}
- index <SigSpec> port(ffcemux, AB) === argQ
- define <bool> pol (AB == \A)
- set ffcepol pol
- semioptional
-endmatch
-
-code argD
- if (ffcemux) {
- dffcemux = ffcemux;
- dffcepol = ffcepol;
- argD = port(ffcemux, ffcepol ? \B : \A);
- dffD.replace(port(ffcemux, \Y), argD);
- }
- else
- dffcemux = nullptr;
+ dffD.replace(argQ, D);
endcode
// #######################
@@ -543,119 +392,26 @@ code
reject;
endcode
-// (1) Starting from an optional $mux cell that implements clock enable
-// semantics --- one where the given 'D' argument (partially or fully)
-// drives one of its two inputs
-match ffcemux
- select ffcemux->type.in($mux)
- // ffcemux output must have two users: ffcemux and ff.D
- select nusers(port(ffcemux, \Y)) == 2
-
- choice <IdString> AB {\A, \B}
- // keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s)
- select nusers(port(ffcemux, AB)) >= 3
-
- slice offset GetSize(port(ffcemux, \Y))
- define <IdString> BA (AB == \A ? \B : \A)
- index <SigBit> port(ffcemux, BA)[offset] === argD[0]
-
- // Check that the rest of argD is present
- filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD)
- filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD
-
- set ffoffset offset
- define <bool> pol (AB == \A)
- set ffcepol pol
-
- semioptional
-endmatch
-
-code argD argQ
- dffcemux = ffcemux;
- if (ffcemux) {
- SigSpec BA = port(ffcemux, ffcepol ? \B : \A);
- SigSpec Y = port(ffcemux, \Y);
- argQ = argD;
- argD.replace(BA, Y);
- argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B));
-
- dffcemux = ffcemux;
- dffcepol = ffcepol;
- }
-endcode
-
-// (2) Starting from, or continuing onto, another optional $mux cell that
-// implements synchronous reset semantics --- one where the given 'D'
-// argument (or the clock enable $mux output) drives one of its two inputs
-// and where the other input is fully zero
-match ffrstmux
- select ffrstmux->type.in($mux)
- // ffrstmux output must have two users: ffrstmux and ff.D
- select nusers(port(ffrstmux, \Y)) == 2
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- slice offset GetSize(port(ffrstmux, \Y))
- define <IdString> AB (BA == \B ? \A : \B)
- index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
-
- // Check that offset is consistent
- filter !ffcemux || ffoffset == offset
- // Check that the rest of argD is present
- filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
- filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
-
- set ffoffset offset
- define <bool> pol (AB == \A)
- set ffrstpol pol
-
- semioptional
-endmatch
-
-code argD argQ
- dffrstmux = ffrstmux;
- if (ffrstmux) {
- SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
- SigSpec Y = port(ffrstmux, \Y);
- argD.replace(AB, Y);
-
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- }
-endcode
-
-// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
-// output of the previous clock enable or reset $mux cells)
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe, $sdff, $sdffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \D)[offset] === argD[0]
- // Check that offset is consistent
- filter (!ffcemux && !ffrstmux) || ffoffset == offset
// Check that the rest of argD is present
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
- // Check that FF.Q is connected to CE-mux
- filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock
-
- set ffoffset offset
endmatch
code argQ
SigSpec D = port(ff, \D);
SigSpec Q = port(ff, \Q);
- if (!ffcemux) {
- argQ = argD;
- argQ.replace(D, Q);
- }
+ argQ = argD;
+ argQ.replace(D, Q);
// Abandon matches when 'Q' has a non-zero init attribute set
// (not supported by DSP48E1)
diff --git a/passes/pmgen/xilinx_dsp_CREG.pmg b/passes/pmgen/xilinx_dsp_CREG.pmg
index 42d4d1b9b..95379771a 100644
--- a/passes/pmgen/xilinx_dsp_CREG.pmg
+++ b/passes/pmgen/xilinx_dsp_CREG.pmg
@@ -26,17 +26,14 @@ pattern xilinx_dsp_packC
udata <std::function<SigSpec(const SigSpec&)>> unextend
state <SigBit> clock
state <SigSpec> sigC sigP
-state <bool> ffCcepol ffCrstpol
-state <Cell*> ffC ffCcemux ffCrstmux
+state <Cell*> ffC
// Variables used for subpatterns
state <SigSpec> argQ argD
-state <bool> ffcepol ffrstpol
state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
-udata <Cell*> dff dffcemux dffrstmux
-udata <bool> dffcepol dffrstpol
+udata <Cell*> dff
// (1) Starting from a DSP48* cell that (a) doesn't have a CREG already,
// and (b) uses the 'C' port
@@ -80,20 +77,12 @@ endcode
// (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
// (attached to at most two $mux cells that implement clock-enable or
// reset functionality, using the in_dffe subpattern)
-code argQ ffC ffCcemux ffCrstmux ffCcepol ffCrstpol sigC clock
+code argQ ffC sigC clock
argQ = sigC;
subpattern(in_dffe);
if (dff) {
ffC = dff;
clock = dffclock;
- if (dffrstmux) {
- ffCrstmux = dffrstmux;
- ffCrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffCcemux = dffcemux;
- ffCcepol = dffcepol;
- }
sigC = dffD;
}
endcode
@@ -106,25 +95,14 @@ endcode
// #######################
// Subpattern for matching against input registers, based on knowledge of the
-// 'Q' input. Typically, identifying registers with clock-enable and reset
-// capability would be a task would be handled by other Yosys passes such as
-// dff2dffe, but since DSP inference happens much before this, these patterns
-// have to be manually identified.
-// At a high level:
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// one that exclusively drives the 'D' input of the $dff, with one of its
-// $mux inputs being fully zero
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
+// 'Q' input.
subpattern in_dffe
-arg argD argQ clock
+arg argQ clock
code
dff = nullptr;
+ if (argQ.empty())
+ reject;
for (const auto &c : argQ.chunks()) {
// Abandon matches when 'Q' is a constant
if (!c.wire)
@@ -135,19 +113,21 @@ code
// Abandon matches when 'Q' has a non-zero init attribute set
// (not supported by DSP48E1)
Const init = c.wire->attributes.at(\init, Const());
- for (auto b : init.extract(c.offset, c.width))
- if (b != State::Sx && b != State::S0)
- reject;
+ if (!init.empty())
+ for (auto b : init.extract(c.offset, c.width))
+ if (b != State::Sx && b != State::S0)
+ reject;
}
endcode
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe, $sdff, $sdffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
+ // Check that reset value, if present, is fully 0.
+ filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero()
+
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
@@ -156,80 +136,14 @@ match ff
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock
-
- set ffoffset offset
endmatch
-code argQ argD
+code argQ
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffD = argQ;
- argD = port(ff, \D);
+ SigSpec D = port(ff, \D);
argQ = Q;
- dffD.replace(argQ, argD);
- // Only search for ffrstmux if dffD only
- // has two (ff, ffrstmux) users
- if (nusers(dffD) > 2)
- argD = SigSpec();
-endcode
-
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// exclusively drives the 'D' input of the $dff, with one of the $mux
-// inputs being fully zero
-match ffrstmux
- if !argD.empty()
- select ffrstmux->type.in($mux)
- index <SigSpec> port(ffrstmux, \Y) === argD
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- define <bool> pol (BA == \B)
- set ffrstpol pol
- semioptional
-endmatch
-
-code argD
- if (ffrstmux) {
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- argD = port(ffrstmux, ffrstpol ? \A : \B);
- dffD.replace(port(ffrstmux, \Y), argD);
-
- // Only search for ffcemux if argQ has at
- // least 3 users (ff, <upstream>, ffrstmux) and
- // dffD only has two (ff, ffrstmux)
- if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
- argD = SigSpec();
- }
- else
- dffrstmux = nullptr;
-endcode
-
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
-match ffcemux
- if !argD.empty()
- select ffcemux->type.in($mux)
- index <SigSpec> port(ffcemux, \Y) === argD
- choice <IdString> AB {\A, \B}
- index <SigSpec> port(ffcemux, AB) === argQ
- define <bool> pol (AB == \A)
- set ffcepol pol
- semioptional
-endmatch
-
-code argD
- if (ffcemux) {
- dffcemux = ffcemux;
- dffcepol = ffcepol;
- argD = port(ffcemux, ffcepol ? \B : \A);
- dffD.replace(port(ffcemux, \Y), argD);
- }
- else
- dffcemux = nullptr;
+ dffD.replace(argQ, D);
endcode
diff --git a/passes/pmgen/xilinx_dsp_cascade.pmg b/passes/pmgen/xilinx_dsp_cascade.pmg
index 8babb88e6..06601554c 100644
--- a/passes/pmgen/xilinx_dsp_cascade.pmg
+++ b/passes/pmgen/xilinx_dsp_cascade.pmg
@@ -51,12 +51,10 @@ state <int> AREG BREG
// Variables used for subpatterns
state <SigSpec> argQ argD
-state <bool> ffcepol ffrstpol
state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
-udata <Cell*> dff dffcemux dffrstmux
-udata <bool> dffcepol dffrstpol
+udata <Cell*> dff
code
#define MAX_DSP_CASCADE 20
@@ -254,9 +252,9 @@ code argQ clock AREG
clock = port(prev, \CLK);
subpattern(in_dffe);
if (dff) {
- if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0)
+ if (!dff->type.in($sdff, $sdffe) && port(prev, \RSTA, State::S0) != State::S0)
goto reject_AREG;
- if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0))
+ if (dff->type.in($sdff, $sdffe) && (port(dff, \SRST) != port(prev, \RSTA, State::S0) || !param(dff, \SRST_POLARITY).as_bool()))
goto reject_AREG;
IdString CEA;
if (param(prev, \AREG) == 1)
@@ -264,9 +262,9 @@ code argQ clock AREG
else if (param(prev, \AREG) == 2)
CEA = \CEA1;
else log_abort();
- if (!dffcemux && port(prev, CEA, State::S0) != State::S1)
+ if (!dff->type.in($dffe, $sdffe) && port(prev, CEA, State::S0) != State::S1)
goto reject_AREG;
- if (dffcemux && port(dffcemux, \S) != port(prev, CEA, State::S0))
+ if (dff->type.in($dffe, $sdffe) && (port(dff, \EN) != port(prev, CEA, State::S0) || !param(dff, \EN_POLARITY).as_bool()))
goto reject_AREG;
if (dffD == unextend(port(prev, \A)))
AREG = 1;
@@ -295,9 +293,9 @@ code argQ clock BREG
clock = port(prev, \CLK);
subpattern(in_dffe);
if (dff) {
- if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0)
+ if (!dff->type.in($sdff, $sdffe) && port(prev, \RSTB, State::S0) != State::S0)
goto reject_BREG;
- if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0))
+ if (dff->type.in($sdff, $sdffe) && (port(dff, \SRST) != port(prev, \RSTB, State::S0) || !param(dff, \SRST_POLARITY).as_bool()))
goto reject_BREG;
IdString CEB;
if (next->type.in(\DSP48A, \DSP48A1))
@@ -310,9 +308,9 @@ code argQ clock BREG
else log_abort();
}
else log_abort();
- if (!dffcemux && port(prev, CEB, State::S0) != State::S1)
+ if (!dff->type.in($dffe, $sdffe) && port(prev, CEB, State::S0) != State::S1)
goto reject_BREG;
- if (dffcemux && port(dffcemux, \S) != port(prev, CEB, State::S0))
+ if (dff->type.in($dffe, $sdffe) && (port(dff, \EN) != port(prev, CEB, State::S0) || !param(dff, \EN_POLARITY).as_bool()))
goto reject_BREG;
if (dffD == unextend(port(prev, \B))) {
if (next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG) != 0)
@@ -357,25 +355,14 @@ endcode
// #######################
// Subpattern for matching against input registers, based on knowledge of the
-// 'Q' input. Typically, identifying registers with clock-enable and reset
-// capability would be a task would be handled by other Yosys passes such as
-// dff2dffe, but since DSP inference happens much before this, these patterns
-// have to be manually identified.
-// At a high level:
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// one that exclusively drives the 'D' input of the $dff, with one of its
-// $mux inputs being fully zero
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
+// 'Q' input.
subpattern in_dffe
-arg argD argQ clock
+arg argQ clock
code
dff = nullptr;
+ if (argQ.empty())
+ reject;
for (const auto &c : argQ.chunks()) {
// Abandon matches when 'Q' is a constant
if (!c.wire)
@@ -386,19 +373,21 @@ code
// Abandon matches when 'Q' has a non-zero init attribute set
// (not supported by DSP48E1)
Const init = c.wire->attributes.at(\init, Const());
- for (auto b : init.extract(c.offset, c.width))
- if (b != State::Sx && b != State::S0)
- reject;
+ if (!init.empty())
+ for (auto b : init.extract(c.offset, c.width))
+ if (b != State::Sx && b != State::S0)
+ reject;
}
endcode
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe, $sdff, $sdffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
+ // Check that reset value, if present, is fully 0.
+ filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero()
+
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
@@ -407,80 +396,14 @@ match ff
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock
-
- set ffoffset offset
endmatch
-code argQ argD
+code argQ
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffD = argQ;
- argD = port(ff, \D);
+ SigSpec D = port(ff, \D);
argQ = Q;
- dffD.replace(argQ, argD);
- // Only search for ffrstmux if dffD only
- // has two (ff, ffrstmux) users
- if (nusers(dffD) > 2)
- argD = SigSpec();
-endcode
-
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// exclusively drives the 'D' input of the $dff, with one of the $mux
-// inputs being fully zero
-match ffrstmux
- if !argD.empty()
- select ffrstmux->type.in($mux)
- index <SigSpec> port(ffrstmux, \Y) === argD
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- define <bool> pol (BA == \B)
- set ffrstpol pol
- semioptional
-endmatch
-
-code argD
- if (ffrstmux) {
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- argD = port(ffrstmux, ffrstpol ? \A : \B);
- dffD.replace(port(ffrstmux, \Y), argD);
-
- // Only search for ffcemux if argQ has at
- // least 3 users (ff, <upstream>, ffrstmux) and
- // dffD only has two (ff, ffrstmux)
- if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
- argD = SigSpec();
- }
- else
- dffrstmux = nullptr;
-endcode
-
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
-match ffcemux
- if !argD.empty()
- select ffcemux->type.in($mux)
- index <SigSpec> port(ffcemux, \Y) === argD
- choice <IdString> AB {\A, \B}
- index <SigSpec> port(ffcemux, AB) === argQ
- define <bool> pol (AB == \A)
- set ffcepol pol
- semioptional
-endmatch
-
-code argD
- if (ffcemux) {
- dffcemux = ffcemux;
- dffcepol = ffcepol;
- argD = port(ffcemux, ffcepol ? \B : \A);
- dffD.replace(port(ffcemux, \Y), argD);
- }
- else
- dffcemux = nullptr;
+ dffD.replace(argQ, D);
endcode
diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc
index f20a167b4..09cf0af82 100644
--- a/passes/proc/proc.cc
+++ b/passes/proc/proc.cc
@@ -50,6 +50,9 @@ struct ProcPass : public Pass {
log("\n");
log("The following options are supported:\n");
log("\n");
+ log(" -nomux\n");
+ log(" Will omit the proc_mux pass.\n");
+ log("\n");
log(" -global_arst [!]<netname>\n");
log(" This option is passed through to proc_arst.\n");
log("\n");
@@ -62,6 +65,7 @@ struct ProcPass : public Pass {
{
std::string global_arst;
bool ifxmode = false;
+ bool nomux = false;
log_header(design, "Executing PROC pass (convert processes to netlists).\n");
log_push();
@@ -69,6 +73,10 @@ struct ProcPass : public Pass {
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
+ if (args[argidx] == "-nomux") {
+ nomux = true;
+ continue;
+ }
if (args[argidx] == "-global_arst" && argidx+1 < args.size()) {
global_arst = args[++argidx];
continue;
@@ -90,7 +98,8 @@ struct ProcPass : public Pass {
Pass::call(design, "proc_arst");
else
Pass::call(design, "proc_arst -global_arst " + global_arst);
- Pass::call(design, ifxmode ? "proc_mux -ifx" : "proc_mux");
+ if (!nomux)
+ Pass::call(design, ifxmode ? "proc_mux -ifx" : "proc_mux");
Pass::call(design, "proc_dlatch");
Pass::call(design, "proc_dff");
Pass::call(design, "proc_clean");
diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc
index 3ed158f37..7b8c05b21 100644
--- a/passes/proc/proc_dlatch.cc
+++ b/passes/proc/proc_dlatch.cc
@@ -19,6 +19,7 @@
#include "kernel/register.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
#include "kernel/consteval.h"
#include "kernel/log.h"
#include <sstream>
@@ -32,15 +33,17 @@ struct proc_dlatch_db_t
{
Module *module;
SigMap sigmap;
+ FfInitVals initvals;
pool<Cell*> generated_dlatches;
dict<Cell*, vector<SigBit>> mux_srcbits;
dict<SigBit, pair<Cell*, int>> mux_drivers;
dict<SigBit, int> sigusers;
- dict<SigBit, std::pair<State,SigBit>> initbits;
proc_dlatch_db_t(Module *module) : module(module), sigmap(module)
{
+ initvals.set(&sigmap, module);
+
for (auto cell : module->cells())
{
if (cell->type.in(ID($mux), ID($pmux)))
@@ -74,29 +77,6 @@ struct proc_dlatch_db_t
if (wire->port_input)
for (auto bit : sigmap(wire))
sigusers[bit]++;
- if (wire->attributes.count(ID::init)) {
- SigSpec wirebits = sigmap(wire);
- Const initval = wire->attributes.at(ID::init);
-
- for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
- {
- SigBit bit = wirebits[i];
- State val = initval[i];
-
- if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
- continue;
-
- if (initbits.count(bit)) {
- if (initbits.at(bit).first != val)
- log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
- log_signal(bit), log_signal(SigBit(wire, i)),
- log_signal(val), log_signal(initbits.at(bit).first));
- continue;
- }
-
- initbits[bit] = std::make_pair(val,SigBit(wire,i));
- }
- }
}
}
@@ -420,11 +400,11 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n",
db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
for (auto &bit : lhs) {
- auto it = db.initbits.find(bit);
- if (it != db.initbits.end()) {
- log("Removing init bit %s for non-memory siginal `%s.%s` in process `%s.%s`.\n", log_signal(it->second.first), db.module->name.c_str(), log_signal(bit), db.module->name.c_str(), proc->name.c_str());
- it->second.second.wire->attributes.at(ID::init)[it->second.second.offset] = State::Sx;
+ State val = db.initvals(bit);
+ if (db.initvals(bit) != State::Sx) {
+ log("Removing init bit %s for non-memory siginal `%s.%s` in process `%s.%s`.\n", log_signal(val), db.module->name.c_str(), log_signal(bit), db.module->name.c_str(), proc->name.c_str());
}
+ db.initvals.remove_init(bit);
}
db.module->connect(lhs, rhs);
offset += chunk.width;
diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc
index 6fc480925..3fa5a614c 100644
--- a/passes/sat/async2sync.cc
+++ b/passes/sat/async2sync.cc
@@ -19,6 +19,8 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
+#include "kernel/ff.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -62,169 +64,183 @@ struct Async2syncPass : public Pass {
for (auto module : design->selected_modules())
{
SigMap sigmap(module);
- dict<SigBit, State> initbits;
- pool<SigBit> del_initbits;
-
- for (auto wire : module->wires())
- if (wire->attributes.count(ID::init) > 0)
- {
- Const initval = wire->attributes.at(ID::init);
- SigSpec initsig = sigmap(wire);
-
- for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
- if (initval[i] == State::S0 || initval[i] == State::S1)
- initbits[initsig[i]] = initval[i];
- }
+ FfInitVals initvals(&sigmap, module);
for (auto cell : vector<Cell*>(module->selected_cells()))
{
- if (cell->type.in(ID($adff)))
- {
- // bool clk_pol = cell->parameters[ID::CLK_POLARITY].as_bool();
- bool arst_pol = cell->parameters[ID::ARST_POLARITY].as_bool();
- Const arst_val = cell->parameters[ID::ARST_VALUE];
-
- // SigSpec sig_clk = cell->getPort(ID::CLK);
- SigSpec sig_arst = cell->getPort(ID::ARST);
- SigSpec sig_d = cell->getPort(ID::D);
- SigSpec sig_q = cell->getPort(ID::Q);
-
- log("Replacing %s.%s (%s): ARST=%s, D=%s, Q=%s\n",
- log_id(module), log_id(cell), log_id(cell->type),
- log_signal(sig_arst), log_signal(sig_d), log_signal(sig_q));
-
- Const init_val;
- for (int i = 0; i < GetSize(sig_q); i++) {
- SigBit bit = sigmap(sig_q[i]);
- init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx);
- del_initbits.insert(bit);
- }
+ if (!RTLIL::builtin_ff_cell_types().count(cell->type))
+ continue;
- Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d));
- Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q));
- new_q->attributes[ID::init] = init_val;
+ FfData ff(&initvals, cell);
- if (arst_pol) {
- module->addMux(NEW_ID, sig_d, arst_val, sig_arst, new_d);
- module->addMux(NEW_ID, new_q, arst_val, sig_arst, sig_q);
- } else {
- module->addMux(NEW_ID, arst_val, sig_d, sig_arst, new_d);
- module->addMux(NEW_ID, arst_val, new_q, sig_arst, sig_q);
- }
-
- cell->setPort(ID::D, new_d);
- cell->setPort(ID::Q, new_q);
- cell->unsetPort(ID::ARST);
- cell->unsetParam(ID::ARST_POLARITY);
- cell->unsetParam(ID::ARST_VALUE);
- cell->type = ID($dff);
+ // Skip for $_FF_ and $ff cells.
+ if (ff.has_d && !ff.has_clk && !ff.has_en)
continue;
- }
- if (cell->type.in(ID($dffsr)))
+ if (ff.has_clk)
{
- // bool clk_pol = cell->parameters[ID::CLK_POLARITY].as_bool();
- bool set_pol = cell->parameters[ID::SET_POLARITY].as_bool();
- bool clr_pol = cell->parameters[ID::CLR_POLARITY].as_bool();
-
- // SigSpec sig_clk = cell->getPort(ID::CLK);
- SigSpec sig_set = cell->getPort(ID::SET);
- SigSpec sig_clr = cell->getPort(ID::CLR);
- SigSpec sig_d = cell->getPort(ID::D);
- SigSpec sig_q = cell->getPort(ID::Q);
-
- log("Replacing %s.%s (%s): SET=%s, CLR=%s, D=%s, Q=%s\n",
- log_id(module), log_id(cell), log_id(cell->type),
- log_signal(sig_set), log_signal(sig_clr), log_signal(sig_d), log_signal(sig_q));
-
- Const init_val;
- for (int i = 0; i < GetSize(sig_q); i++) {
- SigBit bit = sigmap(sig_q[i]);
- init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx);
- del_initbits.insert(bit);
+ if (!ff.has_sr && !ff.has_arst)
+ continue;
+
+ if (ff.has_sr) {
+ ff.unmap_ce_srst(module);
+
+ log("Replacing %s.%s (%s): SET=%s, CLR=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_d), log_signal(ff.sig_q));
+
+ initvals.remove_init(ff.sig_q);
+
+ Wire *new_d = module->addWire(NEW_ID, ff.width);
+ Wire *new_q = module->addWire(NEW_ID, ff.width);
+
+ SigSpec sig_set = ff.sig_set;
+ SigSpec sig_clr = ff.sig_clr;
+
+ if (!ff.pol_set) {
+ if (!ff.is_fine)
+ sig_set = module->Not(NEW_ID, sig_set);
+ else
+ sig_set = module->NotGate(NEW_ID, sig_set);
+ }
+
+ if (ff.pol_clr) {
+ if (!ff.is_fine)
+ sig_clr = module->Not(NEW_ID, sig_clr);
+ else
+ sig_clr = module->NotGate(NEW_ID, sig_clr);
+ }
+
+ if (!ff.is_fine) {
+ SigSpec tmp = module->Or(NEW_ID, ff.sig_d, sig_set);
+ module->addAnd(NEW_ID, tmp, sig_clr, new_d);
+
+ tmp = module->Or(NEW_ID, new_q, sig_set);
+ module->addAnd(NEW_ID, tmp, sig_clr, ff.sig_q);
+ } else {
+ SigSpec tmp = module->OrGate(NEW_ID, ff.sig_d, sig_set);
+ module->addAndGate(NEW_ID, tmp, sig_clr, new_d);
+
+ tmp = module->OrGate(NEW_ID, new_q, sig_set);
+ module->addAndGate(NEW_ID, tmp, sig_clr, ff.sig_q);
+ }
+
+ ff.sig_d = new_d;
+ ff.sig_q = new_q;
+ ff.has_sr = false;
+ } else if (ff.has_arst) {
+ ff.unmap_srst(module);
+
+ log("Replacing %s.%s (%s): ARST=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(ff.sig_arst), log_signal(ff.sig_d), log_signal(ff.sig_q));
+
+ initvals.remove_init(ff.sig_q);
+
+ Wire *new_q = module->addWire(NEW_ID, ff.width);
+
+ if (ff.pol_arst) {
+ if (!ff.is_fine)
+ module->addMux(NEW_ID, new_q, ff.val_arst, ff.sig_arst, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, new_q, ff.val_arst[0], ff.sig_arst, ff.sig_q);
+ } else {
+ if (!ff.is_fine)
+ module->addMux(NEW_ID, ff.val_arst, new_q, ff.sig_arst, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, ff.val_arst[0], new_q, ff.sig_arst, ff.sig_q);
+ }
+
+ ff.sig_q = new_q;
+ ff.has_arst = false;
+ ff.has_srst = true;
+ ff.val_srst = ff.val_arst;
+ ff.sig_srst = ff.sig_arst;
+ ff.pol_srst = ff.pol_arst;
}
-
- Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d));
- Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q));
- new_q->attributes[ID::init] = init_val;
-
- if (!set_pol)
- sig_set = module->Not(NEW_ID, sig_set);
-
- if (clr_pol)
- sig_clr = module->Not(NEW_ID, sig_clr);
-
- SigSpec tmp = module->Or(NEW_ID, sig_d, sig_set);
- module->addAnd(NEW_ID, tmp, sig_clr, new_d);
-
- tmp = module->Or(NEW_ID, new_q, sig_set);
- module->addAnd(NEW_ID, tmp, sig_clr, sig_q);
-
- cell->setPort(ID::D, new_d);
- cell->setPort(ID::Q, new_q);
- cell->unsetPort(ID::SET);
- cell->unsetPort(ID::CLR);
- cell->unsetParam(ID::SET_POLARITY);
- cell->unsetParam(ID::CLR_POLARITY);
- cell->type = ID($dff);
- continue;
}
-
- if (cell->type.in(ID($dlatch)))
+ else
{
- bool en_pol = cell->parameters[ID::EN_POLARITY].as_bool();
-
- SigSpec sig_en = cell->getPort(ID::EN);
- SigSpec sig_d = cell->getPort(ID::D);
- SigSpec sig_q = cell->getPort(ID::Q);
-
+ // Latch.
log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
log_id(module), log_id(cell), log_id(cell->type),
- log_signal(sig_en), log_signal(sig_d), log_signal(sig_q));
-
- Const init_val;
- for (int i = 0; i < GetSize(sig_q); i++) {
- SigBit bit = sigmap(sig_q[i]);
- init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx);
- del_initbits.insert(bit);
+ log_signal(ff.sig_en), log_signal(ff.sig_d), log_signal(ff.sig_q));
+
+ initvals.remove_init(ff.sig_q);
+
+ Wire *new_q = module->addWire(NEW_ID, ff.width);
+ Wire *new_d;
+
+ if (ff.has_d) {
+ new_d = module->addWire(NEW_ID, ff.width);
+ if (ff.pol_en) {
+ if (!ff.is_fine)
+ module->addMux(NEW_ID, new_q, ff.sig_d, ff.sig_en, new_d);
+ else
+ module->addMuxGate(NEW_ID, new_q, ff.sig_d, ff.sig_en, new_d);
+ } else {
+ if (!ff.is_fine)
+ module->addMux(NEW_ID, ff.sig_d, new_q, ff.sig_en, new_d);
+ else
+ module->addMuxGate(NEW_ID, ff.sig_d, new_q, ff.sig_en, new_d);
+ }
+ } else {
+ new_d = new_q;
}
- Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q));
- new_q->attributes[ID::init] = init_val;
-
- if (en_pol) {
- module->addMux(NEW_ID, new_q, sig_d, sig_en, sig_q);
+ if (ff.has_sr) {
+ SigSpec sig_set = ff.sig_set;
+ SigSpec sig_clr = ff.sig_clr;
+
+ if (!ff.pol_set) {
+ if (!ff.is_fine)
+ sig_set = module->Not(NEW_ID, sig_set);
+ else
+ sig_set = module->NotGate(NEW_ID, sig_set);
+ }
+
+ if (ff.pol_clr) {
+ if (!ff.is_fine)
+ sig_clr = module->Not(NEW_ID, sig_clr);
+ else
+ sig_clr = module->NotGate(NEW_ID, sig_clr);
+ }
+
+ if (!ff.is_fine) {
+ SigSpec tmp = module->Or(NEW_ID, new_d, sig_set);
+ module->addAnd(NEW_ID, tmp, sig_clr, ff.sig_q);
+ } else {
+ SigSpec tmp = module->OrGate(NEW_ID, new_d, sig_set);
+ module->addAndGate(NEW_ID, tmp, sig_clr, ff.sig_q);
+ }
+ } else if (ff.has_arst) {
+ if (ff.pol_arst) {
+ if (!ff.is_fine)
+ module->addMux(NEW_ID, new_d, ff.val_arst, ff.sig_arst, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, new_d, ff.val_arst[0], ff.sig_arst, ff.sig_q);
+ } else {
+ if (!ff.is_fine)
+ module->addMux(NEW_ID, ff.val_arst, new_d, ff.sig_arst, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, ff.val_arst[0], new_d, ff.sig_arst, ff.sig_q);
+ }
} else {
- module->addMux(NEW_ID, sig_d, new_q, sig_en, sig_q);
+ module->connect(ff.sig_q, new_d);
}
- cell->setPort(ID::D, sig_q);
- cell->setPort(ID::Q, new_q);
- cell->unsetPort(ID::EN);
- cell->unsetParam(ID::EN_POLARITY);
- cell->type = ID($ff);
- continue;
+ ff.sig_d = new_d;
+ ff.sig_q = new_q;
+ ff.has_en = false;
+ ff.has_arst = false;
+ ff.has_sr = false;
+ ff.has_d = true;
}
- }
- for (auto wire : module->wires())
- if (wire->attributes.count(ID::init) > 0)
- {
- bool delete_initattr = true;
- Const initval = wire->attributes.at(ID::init);
- SigSpec initsig = sigmap(wire);
-
- for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
- if (del_initbits.count(initsig[i]) > 0)
- initval[i] = State::Sx;
- else if (initval[i] != State::Sx)
- delete_initattr = false;
-
- if (delete_initattr)
- wire->attributes.erase(ID::init);
- else
- wire->attributes.at(ID::init) = initval;
- }
+ IdString name = cell->name;
+ module->remove(cell);
+ ff.emit(module, name);
+ }
}
}
} Async2syncPass;
diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc
index cc24db6d4..cbf7c5435 100644
--- a/passes/sat/clk2fflogic.cc
+++ b/passes/sat/clk2fflogic.cc
@@ -19,6 +19,9 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
+#include "kernel/ff.h"
+#include "kernel/mem.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -80,318 +83,173 @@ struct Clk2fflogicPass : public Pass {
for (auto module : design->selected_modules())
{
SigMap sigmap(module);
- dict<SigBit, State> initbits;
- pool<SigBit> del_initbits;
+ FfInitVals initvals(&sigmap, module);
- for (auto wire : module->wires())
- if (wire->attributes.count(ID::init) > 0)
- {
- Const initval = wire->attributes.at(ID::init);
- SigSpec initsig = sigmap(wire);
-
- for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
- if (initval[i] == State::S0 || initval[i] == State::S1)
- initbits[initsig[i]] = initval[i];
+ for (auto &mem : Mem::get_selected_memories(module))
+ {
+ for (int i = 0; i < GetSize(mem.rd_ports); i++) {
+ auto &port = mem.rd_ports[i];
+ if (port.clk_enable)
+ log_error("Read port %d of memory %s.%s is clocked. This is not supported by \"clk2fflogic\"! "
+ "Call \"memory\" with -nordff to avoid this error.\n", i, log_id(mem.memid), log_id(module));
}
- for (auto cell : vector<Cell*>(module->selected_cells()))
- {
- if (cell->type.in(ID($mem)))
+ for (int i = 0; i < GetSize(mem.wr_ports); i++)
{
- int abits = cell->getParam(ID::ABITS).as_int();
- int width = cell->getParam(ID::WIDTH).as_int();
- int rd_ports = cell->getParam(ID::RD_PORTS).as_int();
- int wr_ports = cell->getParam(ID::WR_PORTS).as_int();
-
- for (int i = 0; i < rd_ports; i++) {
- if (cell->getParam(ID::RD_CLK_ENABLE).extract(i).as_bool())
- log_error("Read port %d of memory %s.%s is clocked. This is not supported by \"clk2fflogic\"! "
- "Call \"memory\" with -nordff to avoid this error.\n", i, log_id(cell), log_id(module));
- }
-
- Const wr_clk_en_param = cell->getParam(ID::WR_CLK_ENABLE);
- Const wr_clk_pol_param = cell->getParam(ID::WR_CLK_POLARITY);
-
- SigSpec wr_clk_port = cell->getPort(ID::WR_CLK);
- SigSpec wr_en_port = cell->getPort(ID::WR_EN);
- SigSpec wr_addr_port = cell->getPort(ID::WR_ADDR);
- SigSpec wr_data_port = cell->getPort(ID::WR_DATA);
-
- for (int wport = 0; wport < wr_ports; wport++)
- {
- bool clken = wr_clk_en_param[wport] == State::S1;
- bool clkpol = wr_clk_pol_param[wport] == State::S1;
-
- if (!clken)
- continue;
+ auto &port = mem.wr_ports[i];
- SigBit clk = wr_clk_port[wport];
- SigSpec en = wr_en_port.extract(wport*width, width);
- SigSpec addr = wr_addr_port.extract(wport*abits, abits);
- SigSpec data = wr_data_port.extract(wport*width, width);
+ if (!port.clk_enable)
+ continue;
- log("Modifying write port %d on memory %s.%s: CLK=%s, A=%s, D=%s\n",
- wport, log_id(module), log_id(cell), log_signal(clk),
- log_signal(addr), log_signal(data));
+ log("Modifying write port %d on memory %s.%s: CLK=%s, A=%s, D=%s\n",
+ i, log_id(module), log_id(mem.memid), log_signal(port.clk),
+ log_signal(port.addr), log_signal(port.data));
- Wire *past_clk = module->addWire(NEW_ID);
- past_clk->attributes[ID::init] = clkpol ? State::S1 : State::S0;
- module->addFf(NEW_ID, clk, past_clk);
-
- SigSpec clock_edge_pattern;
-
- if (clkpol) {
- clock_edge_pattern.append(State::S0);
- clock_edge_pattern.append(State::S1);
- } else {
- clock_edge_pattern.append(State::S1);
- clock_edge_pattern.append(State::S0);
- }
-
- SigSpec clock_edge = module->Eqx(NEW_ID, {clk, SigSpec(past_clk)}, clock_edge_pattern);
-
- SigSpec en_q = module->addWire(NEW_ID, GetSize(en));
- module->addFf(NEW_ID, en, en_q);
-
- SigSpec addr_q = module->addWire(NEW_ID, GetSize(addr));
- module->addFf(NEW_ID, addr, addr_q);
-
- SigSpec data_q = module->addWire(NEW_ID, GetSize(data));
- module->addFf(NEW_ID, data, data_q);
+ Wire *past_clk = module->addWire(NEW_ID);
+ past_clk->attributes[ID::init] = port.clk_polarity ? State::S1 : State::S0;
+ module->addFf(NEW_ID, port.clk, past_clk);
- wr_clk_port[wport] = State::S0;
- wr_en_port.replace(wport*width, module->Mux(NEW_ID, Const(0, GetSize(en_q)), en_q, clock_edge));
- wr_addr_port.replace(wport*abits, addr_q);
- wr_data_port.replace(wport*width, data_q);
+ SigSpec clock_edge_pattern;
- wr_clk_en_param[wport] = State::S0;
- wr_clk_pol_param[wport] = State::S0;
+ if (port.clk_polarity) {
+ clock_edge_pattern.append(State::S0);
+ clock_edge_pattern.append(State::S1);
+ } else {
+ clock_edge_pattern.append(State::S1);
+ clock_edge_pattern.append(State::S0);
}
- cell->setParam(ID::WR_CLK_ENABLE, wr_clk_en_param);
- cell->setParam(ID::WR_CLK_POLARITY, wr_clk_pol_param);
+ SigSpec clock_edge = module->Eqx(NEW_ID, {port.clk, SigSpec(past_clk)}, clock_edge_pattern);
- cell->setPort(ID::WR_CLK, wr_clk_port);
- cell->setPort(ID::WR_EN, wr_en_port);
- cell->setPort(ID::WR_ADDR, wr_addr_port);
- cell->setPort(ID::WR_DATA, wr_data_port);
- }
+ SigSpec en_q = module->addWire(NEW_ID, GetSize(port.en));
+ module->addFf(NEW_ID, port.en, en_q);
- if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr)))
- {
- bool enpol = cell->parameters[ID::EN_POLARITY].as_bool();
+ SigSpec addr_q = module->addWire(NEW_ID, GetSize(port.addr));
+ module->addFf(NEW_ID, port.addr, addr_q);
- SigSpec sig_en = cell->getPort(ID::EN);
- SigSpec sig_d = cell->getPort(ID::D);
- SigSpec sig_q = cell->getPort(ID::Q);
+ SigSpec data_q = module->addWire(NEW_ID, GetSize(port.data));
+ module->addFf(NEW_ID, port.data, data_q);
- log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
- log_id(module), log_id(cell), log_id(cell->type),
- log_signal(sig_en), log_signal(sig_d), log_signal(sig_q));
+ port.clk = State::S0;
+ port.en = module->Mux(NEW_ID, Const(0, GetSize(en_q)), en_q, clock_edge);
+ port.addr = addr_q;
+ port.data = data_q;
- sig_en = wrap_async_control(module, sig_en, enpol);
+ port.clk_enable = false;
+ port.clk_polarity = false;
+ }
- Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q));
- module->addFf(NEW_ID, sig_q, past_q);
+ mem.emit();
+ }
- if (cell->type == ID($dlatch))
- {
- module->addMux(NEW_ID, past_q, sig_d, sig_en, sig_q);
- }
- else if (cell->type == ID($adlatch))
- {
- SigSpec t = module->Mux(NEW_ID, past_q, sig_d, sig_en);
- SigSpec arst = wrap_async_control(module, cell->getPort(ID::ARST), cell->parameters[ID::ARST_POLARITY].as_bool());
- Const rstval = cell->parameters[ID::ARST_VALUE];
+ for (auto cell : vector<Cell*>(module->selected_cells()))
+ {
+ SigSpec qval;
+ if (RTLIL::builtin_ff_cell_types().count(cell->type)) {
+ FfData ff(&initvals, cell);
- module->addMux(NEW_ID, t, rstval, arst, sig_q);
+ if (ff.has_d && !ff.has_clk && !ff.has_en) {
+ // Already a $ff or $_FF_ cell.
+ continue;
}
- else
- {
- SigSpec t = module->Mux(NEW_ID, past_q, sig_d, sig_en);
-
- SigSpec s = wrap_async_control(module, cell->getPort(ID::SET), cell->parameters[ID::SET_POLARITY].as_bool());
- t = module->Or(NEW_ID, t, s);
- SigSpec c = wrap_async_control(module, cell->getPort(ID::CLR), cell->parameters[ID::CLR_POLARITY].as_bool());
- c = module->Not(NEW_ID, c);
- module->addAnd(NEW_ID, t, c, sig_q);
+ Wire *past_q = module->addWire(NEW_ID, ff.width);
+ if (!ff.is_fine) {
+ module->addFf(NEW_ID, ff.sig_q, past_q);
+ } else {
+ module->addFfGate(NEW_ID, ff.sig_q, past_q);
}
+ if (!ff.val_init.is_fully_undef())
+ initvals.set_init(past_q, ff.val_init);
- Const initval;
- bool assign_initval = false;
- for (int i = 0; i < GetSize(sig_d); i++) {
- SigBit qbit = sigmap(sig_q[i]);
- if (initbits.count(qbit)) {
- initval.bits.push_back(initbits.at(qbit));
- del_initbits.insert(qbit);
- } else
- initval.bits.push_back(State::Sx);
- if (initval.bits.back() != State::Sx)
- assign_initval = true;
- }
+ if (ff.has_clk) {
+ ff.unmap_ce_srst(module);
- if (assign_initval)
- past_q->attributes[ID::init] = initval;
+ Wire *past_clk = module->addWire(NEW_ID);
+ initvals.set_init(past_clk, ff.pol_clk ? State::S1 : State::S0);
- module->remove(cell);
- continue;
- }
+ if (!ff.is_fine)
+ module->addFf(NEW_ID, ff.sig_clk, past_clk);
+ else
+ module->addFfGate(NEW_ID, ff.sig_clk, past_clk);
- bool word_dff = cell->type.in(ID($dff), ID($adff), ID($dffsr));
- if (word_dff || cell->type.in(ID($_DFF_N_), ID($_DFF_P_),
- ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
- ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_),
- ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
- ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_)))
- {
- bool clkpol;
- SigSpec clk;
- if (word_dff) {
- clkpol = cell->parameters[ID::CLK_POLARITY].as_bool();
- clk = cell->getPort(ID::CLK);
- }
- else {
- if (cell->type.in(ID($_DFF_P_), ID($_DFF_N_),
- ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
- ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_)))
- clkpol = cell->type[6] == 'P';
- else if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
- ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_)))
- clkpol = cell->type[8] == 'P';
- else log_abort();
- clk = cell->getPort(ID::C);
- }
+ log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(ff.sig_clk), log_signal(ff.sig_d), log_signal(ff.sig_q));
- Wire *past_clk = module->addWire(NEW_ID);
- past_clk->attributes[ID::init] = clkpol ? State::S1 : State::S0;
+ SigSpec clock_edge_pattern;
- if (word_dff)
- module->addFf(NEW_ID, clk, past_clk);
- else
- module->addFfGate(NEW_ID, clk, past_clk);
+ if (ff.pol_clk) {
+ clock_edge_pattern.append(State::S0);
+ clock_edge_pattern.append(State::S1);
+ } else {
+ clock_edge_pattern.append(State::S1);
+ clock_edge_pattern.append(State::S0);
+ }
- SigSpec sig_d = cell->getPort(ID::D);
- SigSpec sig_q = cell->getPort(ID::Q);
+ SigSpec clock_edge = module->Eqx(NEW_ID, {ff.sig_clk, SigSpec(past_clk)}, clock_edge_pattern);
- log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
- log_id(module), log_id(cell), log_id(cell->type),
- log_signal(clk), log_signal(sig_d), log_signal(sig_q));
+ Wire *past_d = module->addWire(NEW_ID, ff.width);
+ if (!ff.is_fine)
+ module->addFf(NEW_ID, ff.sig_d, past_d);
+ else
+ module->addFfGate(NEW_ID, ff.sig_d, past_d);
- SigSpec clock_edge_pattern;
+ if (!ff.val_init.is_fully_undef())
+ initvals.set_init(past_d, ff.val_init);
- if (clkpol) {
- clock_edge_pattern.append(State::S0);
- clock_edge_pattern.append(State::S1);
- } else {
- clock_edge_pattern.append(State::S1);
- clock_edge_pattern.append(State::S0);
- }
+ if (!ff.is_fine)
+ qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
+ else
+ qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge);
+ } else if (ff.has_d) {
- SigSpec clock_edge = module->Eqx(NEW_ID, {clk, SigSpec(past_clk)}, clock_edge_pattern);
+ log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(ff.sig_en), log_signal(ff.sig_d), log_signal(ff.sig_q));
- Wire *past_d = module->addWire(NEW_ID, GetSize(sig_d));
- Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q));
- if (word_dff) {
- module->addFf(NEW_ID, sig_d, past_d);
- module->addFf(NEW_ID, sig_q, past_q);
- }
- else {
- module->addFfGate(NEW_ID, sig_d, past_d);
- module->addFfGate(NEW_ID, sig_q, past_q);
- }
+ SigSpec sig_en = wrap_async_control(module, ff.sig_en, ff.pol_en);
- if (cell->type == ID($adff))
- {
- SigSpec arst = wrap_async_control(module, cell->getPort(ID::ARST), cell->parameters[ID::ARST_POLARITY].as_bool());
- SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
- Const rstval = cell->parameters[ID::ARST_VALUE];
+ if (!ff.is_fine)
+ qval = module->Mux(NEW_ID, past_q, ff.sig_d, sig_en);
+ else
+ qval = module->MuxGate(NEW_ID, past_q, ff.sig_d, sig_en);
+ } else {
- module->addMux(NEW_ID, qval, rstval, arst, sig_q);
- }
- else
- if (cell->type.in(ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
- ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_)))
- {
- SigSpec arst = wrap_async_control_gate(module, cell->getPort(ID::R), cell->type[7] == 'P');
- SigSpec qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge);
- SigBit rstval = (cell->type[8] == '1');
-
- module->addMuxGate(NEW_ID, qval, rstval, arst, sig_q);
- }
- else
- if (cell->type == ID($dffsr))
- {
- SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
- SigSpec setval = wrap_async_control(module, cell->getPort(ID::SET), cell->parameters[ID::SET_POLARITY].as_bool());
- SigSpec clrval = wrap_async_control(module, cell->getPort(ID::CLR), cell->parameters[ID::CLR_POLARITY].as_bool());
-
- clrval = module->Not(NEW_ID, clrval);
- qval = module->Or(NEW_ID, qval, setval);
- module->addAnd(NEW_ID, qval, clrval, sig_q);
- }
- else
- if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
- ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_)))
- {
- SigSpec qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge);
- SigSpec setval = wrap_async_control_gate(module, cell->getPort(ID::S), cell->type[9] == 'P');
- SigSpec clrval = wrap_async_control_gate(module, cell->getPort(ID::R), cell->type[10] == 'P');
-
- clrval = module->NotGate(NEW_ID, clrval);
- qval = module->OrGate(NEW_ID, qval, setval);
- module->addAndGate(NEW_ID, qval, clrval, sig_q);
- }
- else if (cell->type == ID($dff))
- {
- module->addMux(NEW_ID, past_q, past_d, clock_edge, sig_q);
- }
- else
- {
- module->addMuxGate(NEW_ID, past_q, past_d, clock_edge, sig_q);
- }
+ log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_q));
- Const initval;
- bool assign_initval = false;
- for (int i = 0; i < GetSize(sig_d); i++) {
- SigBit qbit = sigmap(sig_q[i]);
- if (initbits.count(qbit)) {
- initval.bits.push_back(initbits.at(qbit));
- del_initbits.insert(qbit);
- } else
- initval.bits.push_back(State::Sx);
- if (initval.bits.back() != State::Sx)
- assign_initval = true;
+ qval = past_q;
}
- if (assign_initval) {
- past_d->attributes[ID::init] = initval;
- past_q->attributes[ID::init] = initval;
+ if (ff.has_sr) {
+ SigSpec setval = wrap_async_control(module, ff.sig_set, ff.pol_set);
+ SigSpec clrval = wrap_async_control(module, ff.sig_clr, ff.pol_clr);
+ if (!ff.is_fine) {
+ clrval = module->Not(NEW_ID, clrval);
+ qval = module->Or(NEW_ID, qval, setval);
+ module->addAnd(NEW_ID, qval, clrval, ff.sig_q);
+ } else {
+ clrval = module->NotGate(NEW_ID, clrval);
+ qval = module->OrGate(NEW_ID, qval, setval);
+ module->addAndGate(NEW_ID, qval, clrval, ff.sig_q);
+ }
+ } else if (ff.has_arst) {
+ SigSpec arst = wrap_async_control(module, ff.sig_arst, ff.pol_arst);
+ if (!ff.is_fine)
+ module->addMux(NEW_ID, qval, ff.val_arst, arst, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, qval, ff.val_arst[0], arst, ff.sig_q);
+ } else {
+ module->connect(ff.sig_q, qval);
}
+ initvals.remove_init(ff.sig_q);
module->remove(cell);
continue;
}
}
-
- for (auto wire : module->wires())
- if (wire->attributes.count(ID::init) > 0)
- {
- bool delete_initattr = true;
- Const initval = wire->attributes.at(ID::init);
- SigSpec initsig = sigmap(wire);
-
- for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
- if (del_initbits.count(initsig[i]) > 0)
- initval[i] = State::Sx;
- else if (initval[i] != State::Sx)
- delete_initattr = false;
-
- if (delete_initattr)
- wire->attributes.erase(ID::init);
- else
- wire->attributes.at(ID::init) = initval;
- }
}
}
diff --git a/passes/sat/mutate.cc b/passes/sat/mutate.cc
index 15abee73e..95e0e0944 100644
--- a/passes/sat/mutate.cc
+++ b/passes/sat/mutate.cc
@@ -439,7 +439,7 @@ void mutate_list(Design *design, const mutate_opts_t &opts, const string &filena
dict<SigBit, int> bit_user_cnt;
for (auto wire : module->wires()) {
- if (wire->name[0] == '\\' && wire->attributes.count(ID::src))
+ if (wire->name.isPublic() && wire->attributes.count(ID::src))
sigmap.add(wire);
}
@@ -468,7 +468,7 @@ void mutate_list(Design *design, const mutate_opts_t &opts, const string &filena
}
if (!bit.wire->name[0] != !sigbit.wire->name[0]) {
- if (bit.wire->name[0] == '\\')
+ if (bit.wire->name.isPublic())
sigmap.add(bit);
continue;
}
@@ -493,7 +493,7 @@ void mutate_list(Design *design, const mutate_opts_t &opts, const string &filena
entry.src.insert(s);
SigBit bit = sigmap(conn.second[i]);
- if (bit.wire && bit.wire->name[0] == '\\' && (cell->output(conn.first) || bit_user_cnt[bit] == 1)) {
+ if (bit.wire && bit.wire->name.isPublic() && (cell->output(conn.first) || bit_user_cnt[bit] == 1)) {
for (auto &s : bit.wire->get_strpool_attribute(ID::src))
entry.src.insert(s);
entry.wire = bit.wire->name;
diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc
index d7bf125d1..9fdac6147 100644
--- a/passes/sat/sat.cc
+++ b/passes/sat/sat.cc
@@ -1365,7 +1365,7 @@ struct SatPass : public Pass {
if (show_public) {
for (auto wire : module->wires())
- if (wire->name[0] == '\\')
+ if (wire->name.isPublic())
shows.push_back(wire->name.str());
}
diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc
index fb496ff87..75f922dba 100644
--- a/passes/sat/sim.cc
+++ b/passes/sat/sim.cc
@@ -20,6 +20,9 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
+#include "kernel/mem.h"
+
+#include <ctime>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -62,6 +65,7 @@ struct SimInstance
pool<SigBit> dirty_bits;
pool<Cell*> dirty_cells;
+ pool<IdString> dirty_memories;
pool<SimInstance*, hash_ptr_ops> dirty_children;
struct ff_state_t
@@ -72,16 +76,20 @@ struct SimInstance
struct mem_state_t
{
- Const past_wr_clk;
- Const past_wr_en;
- Const past_wr_addr;
- Const past_wr_data;
+ Mem *mem;
+ std::vector<Const> past_wr_clk;
+ std::vector<Const> past_wr_en;
+ std::vector<Const> past_wr_addr;
+ std::vector<Const> past_wr_data;
Const data;
};
dict<Cell*, ff_state_t> ff_database;
- dict<Cell*, mem_state_t> mem_database;
+ dict<IdString, mem_state_t> mem_database;
pool<Cell*> formal_database;
+ dict<Cell*, IdString> mem_cells;
+
+ std::vector<Mem> memories;
dict<Wire*, pair<int, Const>> vcd_database;
@@ -118,6 +126,19 @@ struct SimInstance
}
}
+ memories = Mem::get_all_memories(module);
+ for (auto &mem : memories) {
+ auto &mdb = mem_database[mem.memid];
+ mdb.mem = &mem;
+ for (auto &port : mem.wr_ports) {
+ mdb.past_wr_clk.push_back(Const(State::Sx));
+ mdb.past_wr_en.push_back(Const(State::Sx, GetSize(port.en)));
+ mdb.past_wr_addr.push_back(Const(State::Sx, GetSize(port.addr)));
+ mdb.past_wr_data.push_back(Const(State::Sx, GetSize(port.data)));
+ }
+ mdb.data = mem.get_init_data();
+ }
+
for (auto cell : module->cells())
{
Module *mod = module->design->module(cell->type);
@@ -143,29 +164,9 @@ struct SimInstance
ff_database[cell] = ff;
}
- if (cell->type == ID($mem))
+ if (cell->type.in(ID($mem), ID($meminit), ID($memwr), ID($memrd)))
{
- mem_state_t mem;
-
- mem.past_wr_clk = Const(State::Sx, GetSize(cell->getPort(ID::WR_CLK)));
- mem.past_wr_en = Const(State::Sx, GetSize(cell->getPort(ID::WR_EN)));
- mem.past_wr_addr = Const(State::Sx, GetSize(cell->getPort(ID::WR_ADDR)));
- mem.past_wr_data = Const(State::Sx, GetSize(cell->getPort(ID::WR_DATA)));
-
- mem.data = cell->getParam(ID::INIT);
- int sz = cell->getParam(ID::SIZE).as_int() * cell->getParam(ID::WIDTH).as_int();
-
- if (GetSize(mem.data) > sz)
- mem.data.bits.resize(sz);
-
- while (GetSize(mem.data) < sz)
- mem.data.bits.push_back(State::Sx);
-
- mem_database[cell] = mem;
- }
- if (cell->type.in(ID($memwr),ID($memrd)))
- {
- log_error("$memrd and $memwr cells have to be merged to stand-alone $mem cells (execute memory_collect pass)\n");
+ mem_cells[cell] = cell->parameters.at(ID::MEMID).decode_string();
}
if (cell->type.in(ID($assert), ID($cover), ID($assume))) {
formal_database.insert(cell);
@@ -188,7 +189,8 @@ struct SimInstance
for (auto &it : mem_database) {
mem_state_t &mem = it.second;
- zinit(mem.past_wr_en);
+ for (auto &val : mem.past_wr_en)
+ zinit(val);
zinit(mem.data);
}
}
@@ -259,37 +261,9 @@ struct SimInstance
if (formal_database.count(cell))
return;
- if (mem_database.count(cell))
+ if (mem_cells.count(cell))
{
- mem_state_t &mem = mem_database.at(cell);
-
- int num_rd_ports = cell->getParam(ID::RD_PORTS).as_int();
-
- int size = cell->getParam(ID::SIZE).as_int();
- int offset = cell->getParam(ID::OFFSET).as_int();
- int abits = cell->getParam(ID::ABITS).as_int();
- int width = cell->getParam(ID::WIDTH).as_int();
-
- if (cell->getParam(ID::RD_CLK_ENABLE).as_bool())
- log_error("Memory %s.%s has clocked read ports. Run 'memory' with -nordff.\n", log_id(module), log_id(cell));
-
- SigSpec rd_addr_sig = cell->getPort(ID::RD_ADDR);
- SigSpec rd_data_sig = cell->getPort(ID::RD_DATA);
-
- for (int port_idx = 0; port_idx < num_rd_ports; port_idx++)
- {
- Const addr = get_state(rd_addr_sig.extract(port_idx*abits, abits));
- Const data = Const(State::Sx, width);
-
- if (addr.is_fully_def()) {
- int index = addr.as_int() - offset;
- if (index >= 0 && index < size)
- data = mem.data.extract(index*width, width);
- }
-
- set_state(rd_data_sig.extract(port_idx*width, width), data);
- }
-
+ dirty_memories.insert(mem_cells[cell]);
return;
}
@@ -352,6 +326,29 @@ struct SimInstance
log_error("Unsupported cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
}
+ void update_memory(IdString id) {
+ auto &mdb = mem_database[id];
+ auto &mem = *mdb.mem;
+
+ for (int port_idx = 0; port_idx < GetSize(mem.rd_ports); port_idx++)
+ {
+ auto &port = mem.rd_ports[port_idx];
+ Const addr = get_state(port.addr);
+ Const data = Const(State::Sx, mem.width);
+
+ if (port.clk_enable)
+ log_error("Memory %s.%s has clocked read ports. Run 'memory' with -nordff.\n", log_id(module), log_id(mem.memid));
+
+ if (addr.is_fully_def()) {
+ int index = addr.as_int() - mem.start_offset;
+ if (index >= 0 && index < mem.size)
+ data = mdb.data.extract(index*mem.width, mem.width);
+ }
+
+ set_state(port.data, data);
+ }
+ }
+
void update_ph1()
{
pool<Cell*> queue_cells;
@@ -383,6 +380,10 @@ struct SimInstance
continue;
}
+ for (auto &memid : dirty_memories)
+ update_memory(memid);
+ dirty_memories.clear();
+
for (auto wire : queue_outports)
if (instance->hasPort(wire->name)) {
Const value = get_state(wire);
@@ -426,50 +427,40 @@ struct SimInstance
for (auto &it : mem_database)
{
- Cell *cell = it.first;
- mem_state_t &mem = it.second;
-
- int num_wr_ports = cell->getParam(ID::WR_PORTS).as_int();
+ mem_state_t &mdb = it.second;
+ auto &mem = *mdb.mem;
- int size = cell->getParam(ID::SIZE).as_int();
- int offset = cell->getParam(ID::OFFSET).as_int();
- int abits = cell->getParam(ID::ABITS).as_int();
- int width = cell->getParam(ID::WIDTH).as_int();
-
- Const wr_clk_enable = cell->getParam(ID::WR_CLK_ENABLE);
- Const wr_clk_polarity = cell->getParam(ID::WR_CLK_POLARITY);
- Const current_wr_clk = get_state(cell->getPort(ID::WR_CLK));
-
- for (int port_idx = 0; port_idx < num_wr_ports; port_idx++)
+ for (int port_idx = 0; port_idx < GetSize(mem.wr_ports); port_idx++)
{
+ auto &port = mem.wr_ports[port_idx];
Const addr, data, enable;
- if (wr_clk_enable[port_idx] == State::S0)
+ if (!port.clk_enable)
{
- addr = get_state(cell->getPort(ID::WR_ADDR).extract(port_idx*abits, abits));
- data = get_state(cell->getPort(ID::WR_DATA).extract(port_idx*width, width));
- enable = get_state(cell->getPort(ID::WR_EN).extract(port_idx*width, width));
+ addr = get_state(port.addr);
+ data = get_state(port.data);
+ enable = get_state(port.en);
}
else
{
- if (wr_clk_polarity[port_idx] == State::S1 ?
- (mem.past_wr_clk[port_idx] == State::S1 || current_wr_clk[port_idx] != State::S1) :
- (mem.past_wr_clk[port_idx] == State::S0 || current_wr_clk[port_idx] != State::S0))
+ if (port.clk_polarity ?
+ (mdb.past_wr_clk[port_idx] == State::S1 || get_state(port.clk) != State::S1) :
+ (mdb.past_wr_clk[port_idx] == State::S0 || get_state(port.clk) != State::S0))
continue;
- addr = mem.past_wr_addr.extract(port_idx*abits, abits);
- data = mem.past_wr_data.extract(port_idx*width, width);
- enable = mem.past_wr_en.extract(port_idx*width, width);
+ addr = mdb.past_wr_addr[port_idx];
+ data = mdb.past_wr_data[port_idx];
+ enable = mdb.past_wr_en[port_idx];
}
if (addr.is_fully_def())
{
- int index = addr.as_int() - offset;
- if (index >= 0 && index < size)
- for (int i = 0; i < width; i++)
- if (enable[i] == State::S1 && mem.data.bits.at(index*width+i) != data[i]) {
- mem.data.bits.at(index*width+i) = data[i];
- dirty_cells.insert(cell);
+ int index = addr.as_int() - mem.start_offset;
+ if (index >= 0 && index < mem.size)
+ for (int i = 0; i < mem.width; i++)
+ if (enable[i] == State::S1 && mdb.data.bits.at(index*mem.width+i) != data[i]) {
+ mdb.data.bits.at(index*mem.width+i) = data[i];
+ dirty_memories.insert(mem.memid);
did_something = true;
}
}
@@ -500,13 +491,15 @@ struct SimInstance
for (auto &it : mem_database)
{
- Cell *cell = it.first;
mem_state_t &mem = it.second;
- mem.past_wr_clk = get_state(cell->getPort(ID::WR_CLK));
- mem.past_wr_en = get_state(cell->getPort(ID::WR_EN));
- mem.past_wr_addr = get_state(cell->getPort(ID::WR_ADDR));
- mem.past_wr_data = get_state(cell->getPort(ID::WR_DATA));
+ for (int i = 0; i < GetSize(mem.mem->wr_ports); i++) {
+ auto &port = mem.mem->wr_ports[i];
+ mem.past_wr_clk[i] = get_state(port.clk);
+ mem.past_wr_en[i] = get_state(port.en);
+ mem.past_wr_addr[i] = get_state(port.addr);
+ mem.past_wr_data[i] = get_state(port.data);
+ }
}
for (auto cell : formal_database)
@@ -561,17 +554,13 @@ struct SimInstance
for (auto &it : mem_database)
{
- Cell *cell = it.first;
mem_state_t &mem = it.second;
- Const initval = mem.data;
-
- while (GetSize(initval) >= 2) {
- if (initval[GetSize(initval)-1] != State::Sx) break;
- if (initval[GetSize(initval)-2] != State::Sx) break;
- initval.bits.pop_back();
- }
-
- cell->setParam(ID::INIT, initval);
+ mem.mem->clear_inits();
+ MemInit minit;
+ minit.addr = mem.mem->start_offset;
+ minit.data = mem.data;
+ mem.mem->inits.push_back(minit);
+ mem.mem->emit();
}
for (auto it : children)
@@ -633,6 +622,7 @@ struct SimWorker : SimShared
SimInstance *top = nullptr;
std::ofstream vcdfile;
pool<IdString> clock, clockn, reset, resetn;
+ std::string timescale;
~SimWorker()
{
@@ -644,6 +634,17 @@ struct SimWorker : SimShared
if (!vcdfile.is_open())
return;
+ vcdfile << stringf("$version %s $end\n", yosys_version_str);
+
+ std::time_t t = std::time(nullptr);
+ char mbstr[255];
+ if (std::strftime(mbstr, sizeof(mbstr), "%c", std::localtime(&t))) {
+ vcdfile << stringf("$date ") << mbstr << stringf(" $end\n");
+ }
+
+ if (!timescale.empty())
+ vcdfile << stringf("$timescale %s $end\n", timescale.c_str());
+
int id = 1;
top->write_vcd_header(vcdfile, id);
@@ -783,6 +784,9 @@ struct SimPass : public Pass {
log(" -zinit\n");
log(" zero-initialize all uninitialized regs and memories\n");
log("\n");
+ log(" -timescale <string>\n");
+ log(" include the specified timescale declaration in the vcd\n");
+ log("\n");
log(" -n <integer>\n");
log(" number of cycles to simulate (default: 20)\n");
log("\n");
@@ -833,6 +837,10 @@ struct SimPass : public Pass {
worker.resetn.insert(RTLIL::escape_id(args[++argidx]));
continue;
}
+ if (args[argidx] == "-timescale" && argidx+1 < args.size()) {
+ worker.timescale = args[++argidx];
+ continue;
+ }
if (args[argidx] == "-a") {
worker.hide_internal = false;
continue;
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index e3b3d8dea..035699603 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -27,7 +27,6 @@ OBJS += passes/techmap/extract_fa.o
OBJS += passes/techmap/extract_counter.o
OBJS += passes/techmap/extract_reduce.o
OBJS += passes/techmap/alumacc.o
-OBJS += passes/techmap/dff2dffe.o
OBJS += passes/techmap/dffinit.o
OBJS += passes/techmap/pmuxtree.o
OBJS += passes/techmap/muxcover.o
@@ -42,7 +41,7 @@ OBJS += passes/techmap/attrmvcp.o
OBJS += passes/techmap/attrmap.o
OBJS += passes/techmap/zinit.o
OBJS += passes/techmap/dfflegalize.o
-OBJS += passes/techmap/dff2dffs.o
+OBJS += passes/techmap/dffunmap.o
OBJS += passes/techmap/flowmap.o
OBJS += passes/techmap/extractinv.o
endif
diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc
index 0a58fdcc0..192e39372 100644
--- a/passes/techmap/abc.cc
+++ b/passes/techmap/abc.cc
@@ -44,6 +44,7 @@
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
+#include "kernel/ffinit.h"
#include "kernel/cost.h"
#include "kernel/log.h"
#include <stdlib.h>
@@ -111,7 +112,7 @@ SigMap assign_map;
RTLIL::Module *module;
std::vector<gate_t> signal_list;
std::map<RTLIL::SigBit, int> signal_map;
-std::map<RTLIL::SigBit, RTLIL::State> signal_init;
+FfInitVals initvals;
pool<std::string> enabled_gates;
bool recover_init, cmos_cost;
@@ -133,10 +134,7 @@ int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1,
gate.in4 = -1;
gate.is_port = false;
gate.bit = bit;
- if (signal_init.count(bit))
- gate.init = signal_init.at(bit);
- else
- gate.init = State::Sx;
+ gate.init = initvals(bit);
signal_list.push_back(gate);
signal_map[bit] = gate.id;
}
@@ -1468,15 +1466,11 @@ struct AbcPass : public Pass {
assign_map.clear();
signal_list.clear();
signal_map.clear();
- signal_init.clear();
+ initvals.clear();
pi_map.clear();
po_map.clear();
-#ifdef ABCEXTERNAL
- std::string exe_file = ABCEXTERNAL;
-#else
- std::string exe_file = proc_self_dirname() + proc_program_prefix() + "yosys-abc";
-#endif
+ std::string exe_file = yosys_abc_executable;
std::string script_file, liberty_file, constr_file, clk_str;
std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1";
bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true;
@@ -1491,13 +1485,6 @@ struct AbcPass : public Pass {
enabled_gates.clear();
cmos_cost = false;
-#ifdef _WIN32
-#ifndef ABCEXTERNAL
- if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\" + proc_program_prefix()+ "yosys-abc.exe"))
- exe_file = proc_self_dirname() + "..\\" + proc_program_prefix() + "yosys-abc";
-#endif
-#endif
-
// get arguments from scratchpad first, then override by command arguments
std::string lut_arg, luts_arg, g_arg;
exe_file = design->scratchpad_get_string("abc.exe", exe_file /* inherit default value if not set */);
@@ -1854,24 +1841,7 @@ struct AbcPass : public Pass {
}
assign_map.set(mod);
- signal_init.clear();
-
- for (Wire *wire : mod->wires())
- if (wire->attributes.count(ID::init)) {
- SigSpec initsig = assign_map(wire);
- Const initval = wire->attributes.at(ID::init);
- for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
- switch (initval[i]) {
- case State::S0:
- signal_init[initsig[i]] = State::S0;
- break;
- case State::S1:
- signal_init[initsig[i]] = State::S1;
- break;
- default:
- break;
- }
- }
+ initvals.set(&assign_map, mod);
if (!dff_mode || !clk_str.empty()) {
abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff,
@@ -2028,7 +1998,7 @@ struct AbcPass : public Pass {
assign_map.clear();
signal_list.clear();
signal_map.clear();
- signal_init.clear();
+ initvals.clear();
pi_map.clear();
po_map.clear();
diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc
index e99c56d8d..7d017ac40 100644
--- a/passes/techmap/abc9.cc
+++ b/passes/techmap/abc9.cc
@@ -295,7 +295,7 @@ struct Abc9Pass : public ScriptPass
run("proc");
run("wbflip");
run("techmap -wb -map %$abc9 -map +/techmap.v A:abc9_flop");
- run("opt");
+ run("opt -nodffe -nosdff");
if (dff_mode || help_mode) {
if (!help_mode)
active_design->scratchpad_unset("abc9_ops.prep_dff_submod.did_something");
diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc
index 7355840aa..b916b049d 100644
--- a/passes/techmap/abc9_exe.cc
+++ b/passes/techmap/abc9_exe.cc
@@ -379,11 +379,7 @@ struct Abc9ExePass : public Pass {
{
log_header(design, "Executing ABC9_EXE pass (technology mapping using ABC9).\n");
-#ifdef ABCEXTERNAL
- std::string exe_file = ABCEXTERNAL;
-#else
- std::string exe_file = proc_self_dirname() + proc_program_prefix()+ "yosys-abc";
-#endif
+ std::string exe_file = yosys_abc_executable;
std::string script_file, clk_str, box_file, lut_file;
std::string delay_target, lutin_shared = "-S 1", wire_delay;
std::string tempdir_name;
@@ -396,13 +392,6 @@ struct Abc9ExePass : public Pass {
show_tempdir = true;
#endif
-#ifdef _WIN32
-#ifndef ABCEXTERNAL
- if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\" + proc_program_prefix() + "yosys-abc.exe"))
- exe_file = proc_self_dirname() + "..\\" + proc_program_prefix() + "yosys-abc";
-#endif
-#endif
-
std::string lut_arg, luts_arg;
exe_file = design->scratchpad_get_string("abc9.exe", exe_file /* inherit default value if not set */);
script_file = design->scratchpad_get_string("abc9.script", script_file);
diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc
deleted file mode 100644
index 62ee3fea6..000000000
--- a/passes/techmap/dff2dffe.cc
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * yosys -- Yosys Open SYnthesis Suite
- *
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
- *
- * 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/celltypes.h"
-#include "passes/techmap/simplemap.h"
-
-USING_YOSYS_NAMESPACE
-PRIVATE_NAMESPACE_BEGIN
-
-struct Dff2dffeWorker
-{
- const dict<IdString, IdString> &direct_dict;
-
- RTLIL::Module *module;
- SigMap sigmap;
- CellTypes ct;
-
- typedef std::pair<RTLIL::Cell*, int> cell_int_t;
- std::map<RTLIL::SigBit, cell_int_t> bit2mux;
- std::vector<RTLIL::Cell*> dff_cells;
- std::map<RTLIL::SigBit, int> bitusers;
-
- typedef std::map<RTLIL::SigBit, bool> pattern_t;
- typedef std::set<pattern_t> patterns_t;
-
-
- Dff2dffeWorker(RTLIL::Module *module, const dict<IdString, IdString> &direct_dict) :
- direct_dict(direct_dict), module(module), sigmap(module), ct(module->design)
- {
- for (auto wire : module->wires()) {
- if (wire->port_output)
- for (auto bit : sigmap(wire))
- bitusers[bit]++;
- }
-
- for (auto cell : module->cells()) {
- if (cell->type.in(ID($mux), ID($pmux), ID($_MUX_))) {
- RTLIL::SigSpec sig_y = sigmap(cell->getPort(ID::Y));
- for (int i = 0; i < GetSize(sig_y); i++)
- bit2mux[sig_y[i]] = cell_int_t(cell, i);
- }
- if (direct_dict.empty()) {
- if (cell->type.in(ID($dff), ID($_DFF_N_), ID($_DFF_P_)))
- dff_cells.push_back(cell);
- } else {
- if (direct_dict.count(cell->type))
- dff_cells.push_back(cell);
- }
- for (auto conn : cell->connections()) {
- if (ct.cell_output(cell->type, conn.first))
- continue;
- for (auto bit : sigmap(conn.second))
- bitusers[bit]++;
- }
- }
- }
-
- patterns_t find_muxtree_feedback_patterns(RTLIL::SigBit d, RTLIL::SigBit q, pattern_t path)
- {
- patterns_t ret;
-
- if (d == q) {
- ret.insert(path);
- return ret;
- }
-
- if (bit2mux.count(d) == 0 || bitusers[d] > 1)
- return ret;
-
- cell_int_t mux_cell_int = bit2mux.at(d);
- RTLIL::SigSpec sig_a = sigmap(mux_cell_int.first->getPort(ID::A));
- RTLIL::SigSpec sig_b = sigmap(mux_cell_int.first->getPort(ID::B));
- RTLIL::SigSpec sig_s = sigmap(mux_cell_int.first->getPort(ID::S));
- int width = GetSize(sig_a), index = mux_cell_int.second;
-
- for (int i = 0; i < GetSize(sig_s); i++)
- if (path.count(sig_s[i]) && path.at(sig_s[i]))
- {
- ret = find_muxtree_feedback_patterns(sig_b[i*width + index], q, path);
-
- if (sig_b[i*width + index] == q) {
- RTLIL::SigSpec s = mux_cell_int.first->getPort(ID::B);
- s[i*width + index] = RTLIL::Sx;
- mux_cell_int.first->setPort(ID::B, s);
- }
-
- return ret;
- }
-
- pattern_t path_else = path;
-
- for (int i = 0; i < GetSize(sig_s); i++)
- {
- if (path.count(sig_s[i]))
- continue;
-
- pattern_t path_this = path;
- path_else[sig_s[i]] = false;
- path_this[sig_s[i]] = true;
-
- for (auto &pat : find_muxtree_feedback_patterns(sig_b[i*width + index], q, path_this))
- ret.insert(pat);
-
- if (sig_b[i*width + index] == q) {
- RTLIL::SigSpec s = mux_cell_int.first->getPort(ID::B);
- s[i*width + index] = RTLIL::Sx;
- mux_cell_int.first->setPort(ID::B, s);
- }
- }
-
- for (auto &pat : find_muxtree_feedback_patterns(sig_a[index], q, path_else))
- ret.insert(pat);
-
- if (sig_a[index] == q) {
- RTLIL::SigSpec s = mux_cell_int.first->getPort(ID::A);
- s[index] = RTLIL::Sx;
- mux_cell_int.first->setPort(ID::A, s);
- }
-
- return ret;
- }
-
- void simplify_patterns(patterns_t&)
- {
- // TBD
- }
-
- RTLIL::SigSpec make_patterns_logic(patterns_t patterns, bool make_gates)
- {
- RTLIL::SigSpec or_input;
-
- for (auto pat : patterns)
- {
- RTLIL::SigSpec s1, s2;
- for (auto it : pat) {
- s1.append(it.first);
- s2.append(it.second);
- }
-
- RTLIL::SigSpec y = module->addWire(NEW_ID);
- RTLIL::Cell *c = module->addNe(NEW_ID, s1, s2, y);
-
- if (make_gates) {
- simplemap(module, c);
- module->remove(c);
- }
-
- or_input.append(y);
- }
-
- if (GetSize(or_input) == 0)
- return State::S1;
-
- if (GetSize(or_input) == 1)
- return or_input;
-
- RTLIL::SigSpec y = module->addWire(NEW_ID);
- RTLIL::Cell *c = module->addReduceAnd(NEW_ID, or_input, y);
-
- if (make_gates) {
- simplemap(module, c);
- module->remove(c);
- }
-
- return y;
- }
-
- void handle_dff_cell(RTLIL::Cell *dff_cell)
- {
- RTLIL::SigSpec sig_d = sigmap(dff_cell->getPort(ID::D));
- RTLIL::SigSpec sig_q = sigmap(dff_cell->getPort(ID::Q));
-
- std::map<patterns_t, std::set<int>> grouped_patterns;
- std::set<int> remaining_indices;
-
- for (int i = 0 ; i < GetSize(sig_d); i++) {
- patterns_t patterns = find_muxtree_feedback_patterns(sig_d[i], sig_q[i], pattern_t());
- if (!patterns.empty()) {
- simplify_patterns(patterns);
- grouped_patterns[patterns].insert(i);
- } else
- remaining_indices.insert(i);
- }
-
- for (auto &it : grouped_patterns) {
- RTLIL::SigSpec new_sig_d, new_sig_q;
- for (int i : it.second) {
- new_sig_d.append(sig_d[i]);
- new_sig_q.append(sig_q[i]);
- }
- if (!direct_dict.empty()) {
- log(" converting %s cell %s to %s for %s -> %s.\n", log_id(dff_cell->type), log_id(dff_cell), log_id(direct_dict.at(dff_cell->type)), log_signal(new_sig_d), log_signal(new_sig_q));
- dff_cell->setPort(ID::E, make_patterns_logic(it.first, true));
- dff_cell->type = direct_dict.at(dff_cell->type);
- } else
- if (dff_cell->type == ID($dff)) {
- RTLIL::Cell *new_cell = module->addDffe(NEW_ID, dff_cell->getPort(ID::CLK), make_patterns_logic(it.first, false),
- new_sig_d, new_sig_q, dff_cell->getParam(ID::CLK_POLARITY).as_bool(), true);
- log(" created $dffe cell %s for %s -> %s.\n", log_id(new_cell), log_signal(new_sig_d), log_signal(new_sig_q));
- } else {
- RTLIL::Cell *new_cell = module->addDffeGate(NEW_ID, dff_cell->getPort(ID::C), make_patterns_logic(it.first, true),
- new_sig_d, new_sig_q, dff_cell->type == ID($_DFF_P_), true);
- log(" created %s cell %s for %s -> %s.\n", log_id(new_cell->type), log_id(new_cell), log_signal(new_sig_d), log_signal(new_sig_q));
- }
- }
-
- if (!direct_dict.empty())
- return;
-
- if (remaining_indices.empty()) {
- log(" removing now obsolete cell %s.\n", log_id(dff_cell));
- module->remove(dff_cell);
- } else if (GetSize(remaining_indices) != GetSize(sig_d)) {
- log(" removing %d now obsolete bits from cell %s.\n", GetSize(sig_d) - GetSize(remaining_indices), log_id(dff_cell));
- RTLIL::SigSpec new_sig_d, new_sig_q;
- for (int i : remaining_indices) {
- new_sig_d.append(sig_d[i]);
- new_sig_q.append(sig_q[i]);
- }
- dff_cell->setPort(ID::D, new_sig_d);
- dff_cell->setPort(ID::Q, new_sig_q);
- dff_cell->setParam(ID::WIDTH, GetSize(remaining_indices));
- }
- }
-
- void run()
- {
- log("Transforming FF to FF+Enable cells in module %s:\n", log_id(module));
- for (auto dff_cell : dff_cells) {
- // log("Handling candidate %s:\n", log_id(dff_cell));
- handle_dff_cell(dff_cell);
- }
- }
-};
-
-struct Dff2dffePass : public Pass {
- Dff2dffePass() : Pass("dff2dffe", "transform $dff cells to $dffe cells") { }
- void help() override
- {
- // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
- log("\n");
- log(" dff2dffe [options] [selection]\n");
- log("\n");
- log("This pass transforms $dff cells driven by a tree of multiplexers with one or\n");
- log("more feedback paths to $dffe cells. It also works on gate-level cells such as\n");
- log("$_DFF_P_, $_DFF_N_ and $_MUX_.\n");
- log("\n");
- log(" -unmap\n");
- log(" operate in the opposite direction: replace $dffe cells with combinations\n");
- log(" of $dff and $mux cells. the options below are ignored in unmap mode.\n");
- log("\n");
- log(" -unmap-mince N\n");
- log(" Same as -unmap but only unmap $dffe where the clock enable port\n");
- log(" signal is used by less $dffe than the specified number\n");
- log("\n");
- log(" -direct <internal_gate_type> <external_gate_type>\n");
- log(" map directly to external gate type. <internal_gate_type> can\n");
- log(" be any internal gate-level FF cell (except $_DFFE_??_). the\n");
- log(" <external_gate_type> is the cell type name for a cell with an\n");
- log(" identical interface to the <internal_gate_type>, except it\n");
- log(" also has an high-active enable port 'E'.\n");
- log(" Usually <external_gate_type> is an intermediate cell type\n");
- log(" that is then translated to the final type using 'techmap'.\n");
- log("\n");
- log(" -direct-match <pattern>\n");
- log(" like -direct for all DFF cell types matching the expression.\n");
- log(" this will use $_DFFE_* as <external_gate_type> matching the\n");
- log(" internal gate type $_DFF_*_, and $_SDFFE_* for those matching\n");
- log(" $_SDFF_*_, except for $_DFF_[NP]_, which is converted to \n");
- log(" $_DFFE_[NP]_.\n");
- log("\n");
- }
- void execute(std::vector<std::string> args, RTLIL::Design *design) override
- {
- log_header(design, "Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n");
-
- bool unmap_mode = false;
- int min_ce_use = -1;
- dict<IdString, IdString> direct_dict;
-
- size_t argidx;
- for (argidx = 1; argidx < args.size(); argidx++) {
- if (args[argidx] == "-unmap") {
- unmap_mode = true;
- continue;
- }
- if (args[argidx] == "-unmap-mince" && argidx + 1 < args.size()) {
- unmap_mode = true;
- min_ce_use = atoi(args[++argidx].c_str());
- continue;
- }
- if (args[argidx] == "-direct" && argidx + 2 < args.size()) {
- string direct_from = RTLIL::escape_id(args[++argidx]);
- string direct_to = RTLIL::escape_id(args[++argidx]);
- direct_dict[direct_from] = direct_to;
- continue;
- }
- if (args[argidx] == "-direct-match" && argidx + 1 < args.size()) {
- bool found_match = false;
- const char *pattern = args[++argidx].c_str();
- if (patmatch(pattern, "$_DFF_P_" )) found_match = true, direct_dict[ID($_DFF_P_) ] = ID($_DFFE_PP_);
- if (patmatch(pattern, "$_DFF_N_" )) found_match = true, direct_dict[ID($_DFF_N_) ] = ID($_DFFE_NP_);
- if (patmatch(pattern, "$_DFF_NN0_")) found_match = true, direct_dict[ID($_DFF_NN0_)] = ID($_DFFE_NN0P_);
- if (patmatch(pattern, "$_DFF_NN1_")) found_match = true, direct_dict[ID($_DFF_NN1_)] = ID($_DFFE_NN1P_);
- if (patmatch(pattern, "$_DFF_NP0_")) found_match = true, direct_dict[ID($_DFF_NP0_)] = ID($_DFFE_NP0P_);
- if (patmatch(pattern, "$_DFF_NP1_")) found_match = true, direct_dict[ID($_DFF_NP1_)] = ID($_DFFE_NP1P_);
- if (patmatch(pattern, "$_DFF_PN0_")) found_match = true, direct_dict[ID($_DFF_PN0_)] = ID($_DFFE_PN0P_);
- if (patmatch(pattern, "$_DFF_PN1_")) found_match = true, direct_dict[ID($_DFF_PN1_)] = ID($_DFFE_PN1P_);
- if (patmatch(pattern, "$_DFF_PP0_")) found_match = true, direct_dict[ID($_DFF_PP0_)] = ID($_DFFE_PP0P_);
- if (patmatch(pattern, "$_DFF_PP1_")) found_match = true, direct_dict[ID($_DFF_PP1_)] = ID($_DFFE_PP1P_);
-
- if (patmatch(pattern, "$_SDFF_NN0_")) found_match = true, direct_dict[ID($_SDFF_NN0_)] = ID($_SDFFE_NN0P_);
- if (patmatch(pattern, "$_SDFF_NN1_")) found_match = true, direct_dict[ID($_SDFF_NN1_)] = ID($_SDFFE_NN1P_);
- if (patmatch(pattern, "$_SDFF_NP0_")) found_match = true, direct_dict[ID($_SDFF_NP0_)] = ID($_SDFFE_NP0P_);
- if (patmatch(pattern, "$_SDFF_NP1_")) found_match = true, direct_dict[ID($_SDFF_NP1_)] = ID($_SDFFE_NP1P_);
- if (patmatch(pattern, "$_SDFF_PN0_")) found_match = true, direct_dict[ID($_SDFF_PN0_)] = ID($_SDFFE_PN0P_);
- if (patmatch(pattern, "$_SDFF_PN1_")) found_match = true, direct_dict[ID($_SDFF_PN1_)] = ID($_SDFFE_PN1P_);
- if (patmatch(pattern, "$_SDFF_PP0_")) found_match = true, direct_dict[ID($_SDFF_PP0_)] = ID($_SDFFE_PP0P_);
- if (patmatch(pattern, "$_SDFF_PP1_")) found_match = true, direct_dict[ID($_SDFF_PP1_)] = ID($_SDFFE_PP1P_);
- if (!found_match)
- log_cmd_error("No cell types matched pattern '%s'.\n", pattern);
- continue;
- }
- break;
- }
- extra_args(args, argidx, design);
-
- if (!direct_dict.empty()) {
- log("Selected cell types for direct conversion:\n");
- for (auto &it : direct_dict)
- log(" %s -> %s\n", log_id(it.first), log_id(it.second));
- }
-
- for (auto mod : design->selected_modules())
- if (!mod->has_processes_warn())
- {
- if (unmap_mode) {
- SigMap sigmap(mod);
- for (auto cell : mod->selected_cells()) {
- if (cell->type == ID($dffe)) {
- if (min_ce_use >= 0) {
- int ce_use = 0;
- for (auto cell_other : mod->selected_cells()) {
- if (cell_other->type != cell->type)
- continue;
- if (sigmap(cell->getPort(ID::EN)) == sigmap(cell_other->getPort(ID::EN)))
- ce_use++;
- }
- if (ce_use >= min_ce_use)
- continue;
- }
-
- RTLIL::SigSpec tmp = mod->addWire(NEW_ID, GetSize(cell->getPort(ID::D)));
- mod->addDff(NEW_ID, cell->getPort(ID::CLK), tmp, cell->getPort(ID::Q), cell->getParam(ID::CLK_POLARITY).as_bool());
- if (cell->getParam(ID::EN_POLARITY).as_bool())
- mod->addMux(NEW_ID, cell->getPort(ID::Q), cell->getPort(ID::D), cell->getPort(ID::EN), tmp);
- else
- mod->addMux(NEW_ID, cell->getPort(ID::D), cell->getPort(ID::Q), cell->getPort(ID::EN), tmp);
- mod->remove(cell);
- continue;
- }
- if (cell->type.begins_with("$_DFFE_")) {
- if (min_ce_use >= 0) {
- int ce_use = 0;
- for (auto cell_other : mod->selected_cells()) {
- if (cell_other->type != cell->type)
- continue;
- if (sigmap(cell->getPort(ID::E)) == sigmap(cell_other->getPort(ID::E)))
- ce_use++;
- }
- if (ce_use >= min_ce_use)
- continue;
- }
-
- bool clk_pol = cell->type.compare(7, 1, "P") == 0;
- bool en_pol = cell->type.compare(8, 1, "P") == 0;
- RTLIL::SigSpec tmp = mod->addWire(NEW_ID);
- mod->addDff(NEW_ID, cell->getPort(ID::C), tmp, cell->getPort(ID::Q), clk_pol);
- if (en_pol)
- mod->addMux(NEW_ID, cell->getPort(ID::Q), cell->getPort(ID::D), cell->getPort(ID::E), tmp);
- else
- mod->addMux(NEW_ID, cell->getPort(ID::D), cell->getPort(ID::Q), cell->getPort(ID::E), tmp);
- mod->remove(cell);
- continue;
- }
- }
- continue;
- }
-
- Dff2dffeWorker worker(mod, direct_dict);
- worker.run();
- }
- }
-} Dff2dffePass;
-
-PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/dff2dffs.cc b/passes/techmap/dff2dffs.cc
deleted file mode 100644
index 6c2cca4bc..000000000
--- a/passes/techmap/dff2dffs.cc
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * yosys -- Yosys Open SYnthesis Suite
- *
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
- * Copyright (C) 2018 David Shah <dave@ds0.me>
- *
- * 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 Dff2dffsPass : public Pass {
- Dff2dffsPass() : Pass("dff2dffs", "process sync set/reset with SR over CE priority") { }
- void help() override
- {
- log("\n");
- log(" dff2dffs [options] [selection]\n");
- log("\n");
- log("Merge synchronous set/reset $_MUX_ cells to create $_SDFF_[NP][NP][01]_, to be run before\n");
- log("dff2dffe for SR over CE priority.\n");
- log("\n");
- log(" -match-init\n");
- log(" Disallow merging synchronous set/reset that has polarity opposite of the\n");
- log(" output wire's init attribute (if any).\n");
- log("\n");
- }
- void execute(std::vector<std::string> args, RTLIL::Design *design) override
- {
- log_header(design, "Executing dff2dffs pass (merge synchronous set/reset into FF cells).\n");
-
- bool match_init = false;
- size_t argidx;
- for (argidx = 1; argidx < args.size(); argidx++)
- {
- // if (args[argidx] == "-singleton") {
- // singleton_mode = true;
- // continue;
- // }
- if (args[argidx] == "-match-init") {
- match_init = true;
- continue;
- }
- break;
- }
- extra_args(args, argidx, design);
-
- pool<IdString> dff_types;
- dff_types.insert(ID($_DFF_N_));
- dff_types.insert(ID($_DFF_P_));
-
- for (auto module : design->selected_modules())
- {
- log("Merging set/reset $_MUX_ cells into DFFs in %s.\n", log_id(module));
-
- SigMap sigmap(module);
- dict<SigBit, Cell*> sr_muxes;
- vector<Cell*> ff_cells;
-
- for (auto cell : module->selected_cells())
- {
- if (dff_types.count(cell->type)) {
- ff_cells.push_back(cell);
- continue;
- }
-
- if (cell->type != ID($_MUX_))
- continue;
-
- SigBit bit_a = sigmap(cell->getPort(ID::A));
- SigBit bit_b = sigmap(cell->getPort(ID::B));
-
- if (bit_a.wire == nullptr || bit_b.wire == nullptr)
- sr_muxes[sigmap(cell->getPort(ID::Y))] = cell;
- }
-
- for (auto cell : ff_cells)
- {
- SigSpec sig_d = cell->getPort(ID::D);
-
- if (GetSize(sig_d) < 1)
- continue;
-
- SigBit bit_d = sigmap(sig_d[0]);
-
- if (sr_muxes.count(bit_d) == 0)
- continue;
-
- Cell *mux_cell = sr_muxes.at(bit_d);
- SigBit bit_a = sigmap(mux_cell->getPort(ID::A));
- SigBit bit_b = sigmap(mux_cell->getPort(ID::B));
- SigBit bit_s = sigmap(mux_cell->getPort(ID::S));
-
- SigBit sr_val, sr_sig;
- bool invert_sr;
- sr_sig = bit_s;
- if (bit_a.wire == nullptr) {
- bit_d = bit_b;
- sr_val = bit_a;
- invert_sr = true;
- } else {
- log_assert(bit_b.wire == nullptr);
- bit_d = bit_a;
- sr_val = bit_b;
- invert_sr = false;
- }
-
- if (match_init) {
- SigBit bit_q = cell->getPort(ID::Q);
- if (bit_q.wire) {
- auto it = bit_q.wire->attributes.find(ID::init);
- if (it != bit_q.wire->attributes.end()) {
- auto init_val = it->second[bit_q.offset];
- if (init_val == State::S1 && sr_val != State::S1)
- continue;
- if (init_val == State::S0 && sr_val != State::S0)
- continue;
- }
- }
- }
-
- log(" Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell),
- log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type));
-
- if (sr_val == State::S1) {
- if (cell->type == ID($_DFF_N_)) {
- if (invert_sr) cell->type = ID($_SDFF_NN1_);
- else cell->type = ID($_SDFF_NP1_);
- } else {
- log_assert(cell->type == ID($_DFF_P_));
- if (invert_sr) cell->type = ID($_SDFF_PN1_);
- else cell->type = ID($_SDFF_PP1_);
- }
- } else {
- if (cell->type == ID($_DFF_N_)) {
- if (invert_sr) cell->type = ID($_SDFF_NN0_);
- else cell->type = ID($_SDFF_NP0_);
- } else {
- log_assert(cell->type == ID($_DFF_P_));
- if (invert_sr) cell->type = ID($_SDFF_PN0_);
- else cell->type = ID($_SDFF_PP0_);
- }
- }
- cell->setPort(ID::R, sr_sig);
- cell->setPort(ID::D, bit_d);
- }
- }
- }
-} Dff2dffsPass;
-
-PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc
index c60a901c1..44af043db 100644
--- a/passes/techmap/dffinit.cc
+++ b/passes/techmap/dffinit.cc
@@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -94,29 +95,10 @@ struct DffinitPass : public Pass {
for (auto module : design->selected_modules())
{
SigMap sigmap(module);
- dict<SigBit, State> init_bits;
- pool<SigBit> cleanup_bits;
- pool<SigBit> used_bits;
-
- for (auto wire : module->selected_wires()) {
- if (wire->attributes.count(ID::init)) {
- Const value = wire->attributes.at(ID::init);
- for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
- if (value[i] != State::Sx)
- init_bits[sigmap(SigBit(wire, i))] = value[i];
- }
- if (wire->port_output)
- for (auto bit : sigmap(wire))
- used_bits.insert(bit);
- }
+ FfInitVals initvals(&sigmap, module);
for (auto cell : module->selected_cells())
{
- for (auto it : cell->connections())
- if (!cell->known() || cell->input(it.first))
- for (auto bit : sigmap(it.second))
- used_bits.insert(bit);
-
if (ff_types.count(cell->type) == 0)
continue;
@@ -131,17 +113,18 @@ struct DffinitPass : public Pass {
if (cell->hasParam(it.second))
value = cell->getParam(it.second);
+ Const initval = initvals(sig);
+ initvals.remove_init(sig);
for (int i = 0; i < GetSize(sig); i++) {
- if (init_bits.count(sig[i]) == 0)
+ if (initval[i] == State::Sx)
continue;
while (GetSize(value.bits) <= i)
value.bits.push_back(State::S0);
- if (noreinit && value.bits[i] != State::Sx && value.bits[i] != init_bits.at(sig[i]))
+ if (noreinit && value.bits[i] != State::Sx && value.bits[i] != initval[i])
log_error("Trying to assign a different init value for %s.%s.%s which technically "
"have a conflicted init value.\n",
log_id(module), log_id(cell), log_id(it.second));
- value.bits[i] = init_bits.at(sig[i]);
- cleanup_bits.insert(sig[i]);
+ value.bits[i] = initval[i];
}
if (highlow_mode && GetSize(value) != 0) {
@@ -161,23 +144,6 @@ struct DffinitPass : public Pass {
}
}
}
-
- for (auto wire : module->selected_wires())
- if (wire->attributes.count(ID::init)) {
- Const &value = wire->attributes.at(ID::init);
- bool do_cleanup = true;
- for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) {
- SigBit bit = sigmap(SigBit(wire, i));
- if (cleanup_bits.count(bit) || !used_bits.count(bit))
- value[i] = State::Sx;
- else if (value[i] != State::Sx)
- do_cleanup = false;
- }
- if (do_cleanup) {
- log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire));
- wire->attributes.erase(ID::init);
- }
- }
}
}
} DffinitPass;
diff --git a/passes/techmap/dfflegalize.cc b/passes/techmap/dfflegalize.cc
index 13ce4f49a..c1e7e557d 100644
--- a/passes/techmap/dfflegalize.cc
+++ b/passes/techmap/dfflegalize.cc
@@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -170,7 +171,7 @@ struct DffLegalizePass : public Pass {
dict<SigBit, int> srst_used;
SigMap sigmap;
- dict<SigBit, std::pair<State,SigBit>> initbits;
+ FfInitVals initvals;
int flip_initmask(int mask) {
int res = mask & INIT_X;
@@ -303,13 +304,7 @@ struct DffLegalizePass : public Pass {
return;
}
- State initval = State::Sx;
- SigBit initbit;
- if (GetSize(sig_q) > 0 && initbits.count(sigmap(sig_q[0]))) {
- const auto &d = initbits.at(sigmap(sig_q[0]));
- initval = d.first;
- initbit = d.second;
- }
+ State initval = initvals(sig_q[0]);
FfInit initmask = INIT_X;
if (initval == State::S0)
@@ -345,12 +340,8 @@ flip_dqi:
sig_d = cell->module->NotGate(NEW_ID, sig_d[0]);
SigBit new_q = SigSpec(cell->module->addWire(NEW_ID))[0];
cell->module->addNotGate(NEW_ID, new_q, sig_q[0]);
- if (initbit.wire) {
- initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
- initbit = new_q;
- new_q.wire->attributes[ID::init] = initval;
- initbits[new_q] = std::make_pair(initval, new_q);
- }
+ initvals.remove_init(sig_q[0]);
+ initvals.set_init(new_q, initval);
sig_q = new_q;
continue;
}
@@ -427,7 +418,8 @@ unmap_enable:
ff_type = has_set ? FF_ADFFE1 : FF_ADFFE0;
break;
}
- if (supported_dffsr & initmask) {
+ if (supported_cells[has_en ? FF_DFFSRE : FF_DFFSR] & initmask) {
+adff_to_dffsr:
// Throw in a set/reset, retry in DFFSR/DFFSRE branch.
if (has_set) {
sig_s = sig_r;
@@ -450,6 +442,9 @@ unmap_enable:
ff_type = has_set ? FF_ADFF1 : FF_ADFF0;
goto unmap_enable;
}
+ if (supported_dffsr & initmask) {
+ goto adff_to_dffsr;
+ }
log_assert(!((has_set ? supported_adff1 : supported_adff0) & initmask));
// Alright, so this particular combination of initval and
// resetval is not natively supported. First, try flipping
@@ -484,15 +479,12 @@ unmap_enable:
}
log_warning("Emulating mismatched async reset and init with several FFs and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name));
- if (initbit.wire)
- initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
+ initvals.remove_init(sig_q[0]);
Wire *adff_q = cell->module->addWire(NEW_ID);
Wire *dff_q = cell->module->addWire(NEW_ID);
Wire *sel_q = cell->module->addWire(NEW_ID);
- dff_q->attributes[ID::init] = initval;
- initbits[SigBit(dff_q, 0)] = std::make_pair(initval, SigBit(dff_q, 0));
- sel_q->attributes[ID::init] = State::S0;
- initbits[SigBit(sel_q, 0)] = std::make_pair(State::S0, SigBit(sel_q, 0));
+ initvals.set_init(SigBit(dff_q, 0), initval);
+ initvals.set_init(SigBit(sel_q, 0), State::S0);
Cell *cell_dff;
Cell *cell_adff;
Cell *cell_sel;
@@ -588,21 +580,15 @@ flip_dqisr:;
}
log_warning("Emulating async set + reset with several FFs and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name));
- if (initbit.wire)
- initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
+ initvals.remove_init(sig_q[0]);
Wire *adff0_q = cell->module->addWire(NEW_ID);
Wire *adff1_q = cell->module->addWire(NEW_ID);
Wire *sel_q = cell->module->addWire(NEW_ID);
- if (init0) {
- adff0_q->attributes[ID::init] = initval;
- initbits[SigBit(adff0_q, 0)] = std::make_pair(initval, SigBit(adff0_q, 0));
- }
- if (init1) {
- adff1_q->attributes[ID::init] = initval;
- initbits[SigBit(adff1_q, 0)] = std::make_pair(initval, SigBit(adff1_q, 0));
- }
- sel_q->attributes[ID::init] = initsel;
- initbits[SigBit(sel_q, 0)] = std::make_pair(initsel, SigBit(sel_q, 0));
+ if (init0)
+ initvals.set_init(SigBit(adff0_q, 0), initval);
+ if (init1)
+ initvals.set_init(SigBit(adff1_q, 0), initval);
+ initvals.set_init(SigBit(sel_q, 0), initsel);
Cell *cell_adff0;
Cell *cell_adff1;
Cell *cell_sel;
@@ -741,15 +727,12 @@ flip_dqisr:;
// The only hope left is breaking down to adff + dff + dlatch + mux.
log_warning("Emulating mismatched async reset and init with several latches and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name));
- if (initbit.wire)
- initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
+ initvals.remove_init(sig_q[0]);
Wire *adlatch_q = cell->module->addWire(NEW_ID);
Wire *dlatch_q = cell->module->addWire(NEW_ID);
Wire *sel_q = cell->module->addWire(NEW_ID);
- dlatch_q->attributes[ID::init] = initval;
- initbits[SigBit(dlatch_q, 0)] = std::make_pair(initval, SigBit(dlatch_q, 0));
- sel_q->attributes[ID::init] = State::S0;
- initbits[SigBit(sel_q, 0)] = std::make_pair(State::S0, SigBit(sel_q, 0));
+ initvals.set_init(SigBit(dlatch_q, 0), initval);
+ initvals.set_init(SigBit(sel_q, 0), State::S0);
Cell *cell_dlatch;
Cell *cell_adlatch;
Cell *cell_sel;
@@ -797,21 +780,15 @@ flip_dqisr:;
}
log_warning("Emulating async set + reset with several latches and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name));
- if (initbit.wire)
- initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
+ initvals.remove_init(sig_q[0]);
Wire *adlatch0_q = cell->module->addWire(NEW_ID);
Wire *adlatch1_q = cell->module->addWire(NEW_ID);
Wire *sel_q = cell->module->addWire(NEW_ID);
- if (init0) {
- adlatch0_q->attributes[ID::init] = initval;
- initbits[SigBit(adlatch0_q, 0)] = std::make_pair(initval, SigBit(adlatch0_q, 0));
- }
- if (init1) {
- adlatch1_q->attributes[ID::init] = initval;
- initbits[SigBit(adlatch1_q, 0)] = std::make_pair(initval, SigBit(adlatch1_q, 0));
- }
- sel_q->attributes[ID::init] = initsel;
- initbits[SigBit(sel_q, 0)] = std::make_pair(initsel, SigBit(sel_q, 0));
+ if (init0)
+ initvals.set_init(SigBit(adlatch0_q, 0), initval);
+ if (init1)
+ initvals.set_init(SigBit(adlatch1_q, 0), initval);
+ initvals.set_init(SigBit(sel_q, 0), initsel);
Cell *cell_adlatch0;
Cell *cell_adlatch1;
Cell *cell_sel;
@@ -1294,35 +1271,7 @@ unrecognized:
for (auto module : design->selected_modules())
{
sigmap.set(module);
- initbits.clear();
-
- for (auto wire : module->wires())
- {
- if (wire->attributes.count(ID::init) == 0)
- continue;
-
- SigSpec wirebits = sigmap(wire);
- Const initval = wire->attributes.at(ID::init);
-
- for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
- {
- SigBit bit = wirebits[i];
- State val = initval[i];
-
- if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
- continue;
-
- if (initbits.count(bit)) {
- if (initbits.at(bit).first != val)
- log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
- log_signal(bit), log_signal(SigBit(wire, i)),
- log_signal(val), log_signal(initbits.at(bit).first));
- continue;
- }
-
- initbits[bit] = std::make_pair(val,SigBit(wire,i));
- }
- }
+ initvals.set(&sigmap, module);
if (mince || minsrst) {
ce_used.clear();
@@ -1365,7 +1314,7 @@ unrecognized:
}
sigmap.clear();
- initbits.clear();
+ initvals.clear();
ce_used.clear();
srst_used.clear();
}
diff --git a/passes/techmap/dffunmap.cc b/passes/techmap/dffunmap.cc
new file mode 100644
index 000000000..fb107ff75
--- /dev/null
+++ b/passes/techmap/dffunmap.cc
@@ -0,0 +1,107 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2020 Marcelina Kościelnicka <mwk@0x04.net>
+ *
+ * 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 DffunmapPass : public Pass {
+ DffunmapPass() : Pass("dffunmap", "unmap clock enable and synchronous reset from FFs") { }
+ void help() override
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" dffunmap [options] [selection]\n");
+ log("\n");
+ log("This pass transforms FF types with clock enable and/or synchronous reset into\n");
+ log("their base type (with neither clock enable nor sync reset) by emulating the clock\n");
+ log("enable and synchronous reset with multiplexers on the cell input.\n");
+ log("\n");
+ log(" -ce-only\n");
+ log(" unmap only clock enables, leave synchronous resets alone.\n");
+ log("\n");
+ log(" -srst-only\n");
+ log(" unmap only synchronous resets, leave clock enables alone.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
+ {
+ log_header(design, "Executing DFFUNMAP pass (unmap clock enable and synchronous reset from FFs).\n");
+
+ bool ce_only = false;
+ bool srst_only = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-ce-only") {
+ ce_only = true;
+ continue;
+ }
+ if (args[argidx] == "-srst-only") {
+ srst_only = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (ce_only && srst_only)
+ log_cmd_error("Options -ce-only and -srst-only are mutually exclusive!\n");
+
+ for (auto mod : design->selected_modules())
+ {
+ SigMap sigmap(mod);
+ FfInitVals initvals(&sigmap, mod);
+
+ for (auto cell : mod->selected_cells())
+ {
+ if (!RTLIL::builtin_ff_cell_types().count(cell->type))
+ continue;
+
+ FfData ff(&initvals, cell);
+ IdString name = cell->name;
+
+ if (!ff.has_clk)
+ continue;
+
+ if (ce_only) {
+ if (!ff.has_en)
+ continue;
+ ff.unmap_ce(mod);
+ } else if (srst_only) {
+ if (!ff.has_srst)
+ continue;
+ ff.unmap_srst(mod);
+ } else {
+ if (!ff.has_en && !ff.has_srst)
+ continue;
+ ff.unmap_ce_srst(mod);
+ }
+
+ mod->remove(cell);
+ ff.emit(mod, name);
+ }
+ }
+ }
+} DffunmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc
index 7278cb680..f5966fac0 100644
--- a/passes/techmap/extract.cc
+++ b/passes/techmap/extract.cc
@@ -354,7 +354,7 @@ struct ExtractPass : public Pass {
log("\n");
log("This pass looks for subcircuits that are isomorphic to any of the modules\n");
log("in the given map file and replaces them with instances of this modules. The\n");
- log("map file can be a Verilog source file (*.v) or an ilang file (*.il).\n");
+ log("map file can be a Verilog source file (*.v) or an RTLIL source file (*.il).\n");
log("\n");
log(" -map <map_file>\n");
log(" use the modules in this file as reference. This option can be used\n");
@@ -409,7 +409,7 @@ struct ExtractPass : public Pass {
log("the following options are to be used instead of the -map option.\n");
log("\n");
log(" -mine <out_file>\n");
- log(" mine for frequent subcircuits and write them to the given ilang file\n");
+ log(" mine for frequent subcircuits and write them to the given RTLIL file\n");
log("\n");
log(" -mine_cells_span <min> <max>\n");
log(" only mine for subcircuits with the specified number of cells\n");
@@ -578,7 +578,7 @@ struct ExtractPass : public Pass {
}
if (map_filenames.empty() && mine_outfile.empty())
- log_cmd_error("Missing option -map <verilog_or_ilang_file> or -mine <output_ilang_file>.\n");
+ log_cmd_error("Missing option -map <verilog_or_rtlil_file> or -mine <output_rtlil_file>.\n");
RTLIL::Design *map = nullptr;
@@ -606,7 +606,7 @@ struct ExtractPass : public Pass {
delete map;
log_cmd_error("Can't open map file `%s'.\n", filename.c_str());
}
- Frontend::frontend_call(map, &f, filename, (filename.size() > 3 && filename.compare(filename.size()-3, std::string::npos, ".il") == 0 ? "ilang" : "verilog"));
+ Frontend::frontend_call(map, &f, filename, (filename.size() > 3 && filename.compare(filename.size()-3, std::string::npos, ".il") == 0 ? "rtlil" : "verilog"));
f.close();
if (filename.size() <= 3 || filename.compare(filename.size()-3, std::string::npos, ".il") != 0) {
@@ -744,7 +744,7 @@ struct ExtractPass : public Pass {
f.open(mine_outfile.c_str(), std::ofstream::trunc);
if (f.fail())
log_error("Can't open output file `%s'.\n", mine_outfile.c_str());
- Backend::backend_call(map, &f, mine_outfile, "ilang");
+ Backend::backend_call(map, &f, mine_outfile, "rtlil");
f.close();
}
diff --git a/passes/techmap/flatten.cc b/passes/techmap/flatten.cc
index b5f55cffa..08978f446 100644
--- a/passes/techmap/flatten.cc
+++ b/passes/techmap/flatten.cc
@@ -152,15 +152,14 @@ struct FlattenWorker
// Attach port connections of the flattened cell
- SigMap tpl_sigmap(tpl);
pool<SigBit> tpl_driven;
for (auto tpl_cell : tpl->cells())
for (auto &tpl_conn : tpl_cell->connections())
if (tpl_cell->output(tpl_conn.first))
- for (auto bit : tpl_sigmap(tpl_conn.second))
+ for (auto bit : tpl_conn.second)
tpl_driven.insert(bit);
for (auto &tpl_conn : tpl->connections())
- for (auto bit : tpl_sigmap(tpl_conn.first))
+ for (auto bit : tpl_conn.first)
tpl_driven.insert(bit);
SigMap sigmap(module);
@@ -190,7 +189,7 @@ struct FlattenWorker
} else {
SigSpec sig_tpl = tpl_wire, sig_mod = port_it.second;
for (int i = 0; i < GetSize(sig_tpl) && i < GetSize(sig_mod); i++) {
- if (tpl_driven.count(tpl_sigmap(sig_tpl[i]))) {
+ if (tpl_driven.count(sig_tpl[i])) {
new_conn.first.append(sig_mod[i]);
new_conn.second.append(sig_tpl[i]);
} else {
diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc
index 237c261ae..b971068f7 100644
--- a/passes/techmap/shregmap.cc
+++ b/passes/techmap/shregmap.cc
@@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -100,9 +101,8 @@ struct ShregmapWorker
int dff_count, shreg_count;
pool<Cell*> remove_cells;
- pool<SigBit> remove_init;
- dict<SigBit, bool> sigbit_init;
+ FfInitVals initvals;
dict<SigBit, Cell*> sigbit_chain_next;
dict<SigBit, Cell*> sigbit_chain_prev;
pool<SigBit> sigbit_with_non_chain_users;
@@ -116,16 +116,6 @@ struct ShregmapWorker
for (auto bit : sigmap(wire))
sigbit_with_non_chain_users.insert(bit);
}
-
- if (wire->attributes.count(ID::init)) {
- SigSpec initsig = sigmap(wire);
- Const initval = wire->attributes.at(ID::init);
- for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
- if (initval[i] == State::S0 && !opts.zinit)
- sigbit_init[initsig[i]] = false;
- else if (initval[i] == State::S1)
- sigbit_init[initsig[i]] = true;
- }
}
for (auto cell : module->cells())
@@ -137,8 +127,9 @@ struct ShregmapWorker
SigBit d_bit = sigmap(cell->getPort(d_port).as_bit());
SigBit q_bit = sigmap(cell->getPort(q_port).as_bit());
+ State initval = initvals(q_bit);
- if (opts.init || sigbit_init.count(q_bit) == 0)
+ if (opts.init || initval == State::Sx || (opts.zinit && initval == State::S0))
{
auto r = sigbit_chain_next.insert(std::make_pair(d_bit, cell));
if (!r.second) {
@@ -310,22 +301,17 @@ struct ShregmapWorker
if (opts.init) {
vector<State> initval;
for (int i = depth-1; i >= 0; i--) {
- SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit());
- if (sigbit_init.count(bit) == 0)
- initval.push_back(State::Sx);
- else if (sigbit_init.at(bit))
- initval.push_back(State::S1);
- else
- initval.push_back(State::S0);
- remove_init.insert(bit);
+ SigBit bit = chain[cursor+i]->getPort(q_port).as_bit();
+ initval.push_back(initvals(bit));
+ initvals.remove_init(bit);
}
first_cell->setParam(ID::INIT, initval);
}
if (opts.zinit)
for (int i = depth-1; i >= 0; i--) {
- SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit());
- remove_init.insert(bit);
+ SigBit bit = chain[cursor+i]->getPort(q_port).as_bit();
+ initvals.remove_init(bit);
}
if (opts.params)
@@ -364,22 +350,6 @@ struct ShregmapWorker
for (auto cell : remove_cells)
module->remove(cell);
- for (auto wire : module->wires())
- {
- if (wire->attributes.count(ID::init) == 0)
- continue;
-
- SigSpec initsig = sigmap(wire);
- Const &initval = wire->attributes.at(ID::init);
-
- for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
- if (remove_init.count(initsig[i]))
- initval[i] = State::Sx;
-
- if (SigSpec(initval).is_fully_undef())
- wire->attributes.erase(ID::init);
- }
-
remove_cells.clear();
sigbit_chain_next.clear();
sigbit_chain_prev.clear();
@@ -389,6 +359,7 @@ struct ShregmapWorker
ShregmapWorker(Module *module, const ShregmapOptions &opts) :
module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
{
+ initvals.set(&sigmap, module);
make_sigbit_chain_next_prev();
find_chain_start_cells();
diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc
index f98d1564a..d43737c8d 100644
--- a/passes/techmap/techmap.cc
+++ b/passes/techmap/techmap.cc
@@ -20,6 +20,7 @@
#include "kernel/yosys.h"
#include "kernel/utils.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
#include "libs/sha1/sha1.h"
#include <stdlib.h>
@@ -232,16 +233,14 @@ struct TechmapWorker
}
}
- SigMap tpl_sigmap(tpl);
pool<SigBit> tpl_written_bits;
-
for (auto tpl_cell : tpl->cells())
for (auto &conn : tpl_cell->connections())
if (tpl_cell->output(conn.first))
- for (auto bit : tpl_sigmap(conn.second))
+ for (auto bit : conn.second)
tpl_written_bits.insert(bit);
for (auto &conn : tpl->connections())
- for (auto bit : tpl_sigmap(conn.first))
+ for (auto bit : conn.first)
tpl_written_bits.insert(bit);
SigMap port_signal_map;
@@ -279,7 +278,7 @@ struct TechmapWorker
SigSpec sig_tpl = w, sig_tpl_pf = w, sig_mod = it.second;
apply_prefix(cell->name, sig_tpl_pf, module);
for (int i = 0; i < GetSize(sig_tpl) && i < GetSize(sig_mod); i++) {
- if (tpl_written_bits.count(tpl_sigmap(sig_tpl[i]))) {
+ if (tpl_written_bits.count(sig_tpl[i])) {
c.first.append(sig_mod[i]);
c.second.append(sig_tpl_pf[i]);
} else {
@@ -426,18 +425,7 @@ struct TechmapWorker
LogMakeDebugHdl mkdebug;
SigMap sigmap(module);
-
- dict<SigBit, State> init_bits;
- pool<SigBit> remove_init_bits;
-
- for (auto wire : module->wires()) {
- if (wire->attributes.count(ID::init)) {
- Const value = wire->attributes.at(ID::init);
- for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
- if (value[i] != State::Sx)
- init_bits[sigmap(SigBit(wire, i))] = value[i];
- }
- }
+ FfInitVals initvals(&sigmap, module);
TopoSort<RTLIL::Cell*, IdString::compare_ptr_by_name<RTLIL::Cell>> cells;
dict<RTLIL::Cell*, pool<RTLIL::SigBit>> cell_to_inbit;
@@ -643,6 +631,8 @@ struct TechmapWorker
if (tpl->avail_parameters.count(ID::_TECHMAP_CELLTYPE_) != 0)
parameters.emplace(ID::_TECHMAP_CELLTYPE_, RTLIL::unescape_id(cell->type));
+ if (tpl->avail_parameters.count(ID::_TECHMAP_CELLNAME_) != 0)
+ parameters.emplace(ID::_TECHMAP_CELLNAME_, RTLIL::unescape_id(cell->name));
for (auto &conn : cell->connections()) {
if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTMSK_%s_", log_id(conn.first))) != 0) {
@@ -659,15 +649,7 @@ struct TechmapWorker
parameters.emplace(stringf("\\_TECHMAP_CONSTVAL_%s_", log_id(conn.first)), RTLIL::SigSpec(v).as_const());
}
if (tpl->avail_parameters.count(stringf("\\_TECHMAP_WIREINIT_%s_", log_id(conn.first))) != 0) {
- auto sig = sigmap(conn.second);
- RTLIL::Const value(State::Sx, sig.size());
- for (int i = 0; i < sig.size(); i++) {
- auto it = init_bits.find(sig[i]);
- if (it != init_bits.end()) {
- value[i] = it->second;
- }
- }
- parameters.emplace(stringf("\\_TECHMAP_WIREINIT_%s_", log_id(conn.first)), value);
+ parameters.emplace(stringf("\\_TECHMAP_WIREINIT_%s_", log_id(conn.first)), initvals(conn.second));
}
}
@@ -817,11 +799,31 @@ struct TechmapWorker
}
}
+ // Handle outputs first, as these cannot be remapped.
+ for (auto &conn : cell->connections())
+ {
+ Wire *twire = tpl->wire(conn.first);
+ if (!twire->port_output)
+ continue;
+
+ for (int i = 0; i < GetSize(conn.second); i++) {
+ RTLIL::SigBit bit = sigmap(conn.second[i]);
+ RTLIL::SigBit tplbit(twire, i);
+ cellbits_to_tplbits[bit] = tplbit;
+ }
+ }
+
+ // Now handle inputs, remapping as necessary.
for (auto &conn : cell->connections())
+ {
+ Wire *twire = tpl->wire(conn.first);
+ if (twire->port_output)
+ continue;
+
for (int i = 0; i < GetSize(conn.second); i++)
{
RTLIL::SigBit bit = sigmap(conn.second[i]);
- RTLIL::SigBit tplbit(tpl->wire(conn.first), i);
+ RTLIL::SigBit tplbit(twire, i);
if (bit.wire == nullptr)
{
@@ -836,6 +838,7 @@ struct TechmapWorker
else
cellbits_to_tplbits[bit] = tplbit;
}
+ }
RTLIL::SigSig port_conn;
for (auto &it : port_connmap) {
@@ -912,7 +915,7 @@ struct TechmapWorker
auto sig = sigmap(it->second);
for (int i = 0; i < sig.size(); i++)
if (val[i] == State::S1)
- remove_init_bits.insert(sig[i]);
+ initvals.remove_init(sig[i]);
}
}
}
@@ -961,25 +964,6 @@ struct TechmapWorker
handled_cells.insert(cell);
}
- if (!remove_init_bits.empty()) {
- for (auto wire : module->wires())
- if (wire->attributes.count(ID::init)) {
- Const &value = wire->attributes.at(ID::init);
- bool do_cleanup = true;
- for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) {
- SigBit bit = sigmap(SigBit(wire, i));
- if (remove_init_bits.count(bit))
- value[i] = State::Sx;
- else if (value[i] != State::Sx)
- do_cleanup = false;
- }
- if (do_cleanup) {
- log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire));
- wire->attributes.erase(ID::init);
- }
- }
- }
-
if (log_continue) {
log_header(design, "Continuing TECHMAP pass.\n");
log_continue = false;
@@ -999,7 +983,7 @@ struct TechmapPass : public Pass {
log(" techmap [-map filename] [selection]\n");
log("\n");
log("This pass implements a very simple technology mapper that replaces cells in\n");
- log("the design with implementations given in form of a Verilog or ilang source\n");
+ log("the design with implementations given in form of a Verilog or RTLIL source\n");
log("file.\n");
log("\n");
log(" -map filename\n");
@@ -1042,7 +1026,9 @@ struct TechmapPass : public Pass {
log("\n");
log("When a module in the map file has the 'techmap_celltype' attribute set, it will\n");
log("match cells with a type that match the text value of this attribute. Otherwise\n");
- log("the module name will be used to match the cell.\n");
+ log("the module name will be used to match the cell. Multiple space-separated cell\n");
+ log("types can be listed, and wildcards using [] will be expanded (ie. \"$_DFF_[PN]_\"\n");
+ log("is the same as \"$_DFF_P_ $_DFF_N_\").\n");
log("\n");
log("When a module in the map file has the 'techmap_simplemap' attribute set, techmap\n");
log("will use 'simplemap' (see 'help simplemap') to map cells matching the module.\n");
@@ -1111,6 +1097,10 @@ struct TechmapPass : public Pass {
log(" When a parameter with this name exists, it will be set to the type name\n");
log(" of the cell that matches the module.\n");
log("\n");
+ log(" _TECHMAP_CELLNAME_\n");
+ log(" When a parameter with this name exists, it will be set to the name\n");
+ log(" of the cell that matches the module.\n");
+ log("\n");
log(" _TECHMAP_CONSTMSK_<port-name>_\n");
log(" _TECHMAP_CONSTVAL_<port-name>_\n");
log(" When this pair of parameters is available in a module for a port, then\n");
@@ -1220,7 +1210,7 @@ struct TechmapPass : public Pass {
if (!map->module(mod->name))
map->add(mod->clone());
} else {
- Frontend::frontend_call(map, nullptr, fn, (fn.size() > 3 && fn.compare(fn.size()-3, std::string::npos, ".il") == 0 ? "ilang" : verilog_frontend));
+ Frontend::frontend_call(map, nullptr, fn, (fn.size() > 3 && fn.compare(fn.size()-3, std::string::npos, ".il") == 0 ? "rtlil" : verilog_frontend));
}
}
@@ -1230,8 +1220,27 @@ struct TechmapPass : public Pass {
for (auto module : map->modules()) {
if (module->attributes.count(ID::techmap_celltype) && !module->attributes.at(ID::techmap_celltype).bits.empty()) {
char *p = strdup(module->attributes.at(ID::techmap_celltype).decode_string().c_str());
- for (char *q = strtok(p, " \t\r\n"); q; q = strtok(nullptr, " \t\r\n"))
- celltypeMap[RTLIL::escape_id(q)].insert(module->name);
+ for (char *q = strtok(p, " \t\r\n"); q; q = strtok(nullptr, " \t\r\n")) {
+ std::vector<std::string> queue;
+ queue.push_back(q);
+ while (!queue.empty()) {
+ std::string name = queue.back();
+ queue.pop_back();
+ auto pos = name.find('[');
+ if (pos == std::string::npos) {
+ // No further expansion.
+ celltypeMap[RTLIL::escape_id(name)].insert(module->name);
+ } else {
+ // Expand [] in this name.
+ auto epos = name.find(']', pos);
+ if (epos == std::string::npos)
+ log_error("Malformed techmap_celltype pattern %s\n", q);
+ for (size_t i = pos + 1; i < epos; i++) {
+ queue.push_back(name.substr(0, pos) + name[i] + name.substr(epos + 1, std::string::npos));
+ }
+ }
+ }
+ }
free(p);
} else {
IdString module_name = module->name.begins_with("\\$") ?
@@ -1239,8 +1248,15 @@ struct TechmapPass : public Pass {
celltypeMap[module_name].insert(module->name);
}
}
- for (auto &i : celltypeMap)
+ log_debug("Cell type mappings to use:\n");
+ for (auto &i : celltypeMap) {
i.second.sort(RTLIL::sort_by_id_str());
+ std::string maps = "";
+ for (auto &map : i.second)
+ maps += stringf(" %s", log_id(map));
+ log_debug(" %s:%s\n", log_id(i.first), maps.c_str());
+ }
+ log_debug("\n");
for (auto module : design->modules())
worker.module_queue.insert(module);
diff --git a/passes/techmap/zinit.cc b/passes/techmap/zinit.cc
index cc0b26bcc..e3b4ae573 100644
--- a/passes/techmap/zinit.cc
+++ b/passes/techmap/zinit.cc
@@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -57,35 +58,7 @@ struct ZinitPass : public Pass {
for (auto module : design->selected_modules())
{
SigMap sigmap(module);
- dict<SigBit, std::pair<State,SigBit>> initbits;
-
- for (auto wire : module->selected_wires())
- {
- if (wire->attributes.count(ID::init) == 0)
- continue;
-
- SigSpec wirebits = sigmap(wire);
- Const initval = wire->attributes.at(ID::init);
-
- for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
- {
- SigBit bit = wirebits[i];
- State val = initval[i];
-
- if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
- continue;
-
- if (initbits.count(bit)) {
- if (initbits.at(bit).first != val)
- log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
- log_signal(bit), log_signal(SigBit(wire, i)),
- log_signal(val), log_signal(initbits.at(bit).first));
- continue;
- }
-
- initbits[bit] = std::make_pair(val,SigBit(wire,i));
- }
- }
+ FfInitVals initvals(&sigmap, module);
pool<IdString> dff_types = {
// FIXME: It would appear that supporting
@@ -127,33 +100,28 @@ struct ZinitPass : public Pass {
if (GetSize(sig_d) < 1 || GetSize(sig_q) < 1)
continue;
- Const initval;
+ Const initval = initvals(sig_q);
+ Const newval = initval;
+ initvals.remove_init(sig_q);
- for (int i = 0; i < GetSize(sig_q); i++) {
- if (initbits.count(sig_q[i])) {
- const auto &d = initbits.at(sig_q[i]);
- initval.bits.push_back(d.first);
- const auto &b = d.second;
- b.wire->attributes.at(ID::init)[b.offset] = State::Sx;
- } else
- initval.bits.push_back(all_mode ? State::S0 : State::Sx);
- }
-
- Wire *initwire = module->addWire(NEW_ID, GetSize(initval));
- initwire->attributes[ID::init] = initval;
+ Wire *initwire = module->addWire(NEW_ID, GetSize(sig_q));
for (int i = 0; i < GetSize(initwire); i++)
if (initval[i] == State::S1)
{
sig_d[i] = module->NotGate(NEW_ID, sig_d[i]);
module->addNotGate(NEW_ID, SigSpec(initwire, i), sig_q[i]);
- initwire->attributes[ID::init][i] = State::S0;
+ newval[i] = State::S0;
}
else
{
module->connect(sig_q[i], SigSpec(initwire, i));
+ if (all_mode)
+ newval[i] = State::S0;
}
+ initvals.set_init(initwire, newval);
+
log("FF init value for cell %s (%s): %s = %s\n", log_id(cell), log_id(cell->type),
log_signal(sig_q), log_signal(initval));
diff --git a/passes/tests/test_abcloop.cc b/passes/tests/test_abcloop.cc
index 2d80e66e4..ac31e36f1 100644
--- a/passes/tests/test_abcloop.cc
+++ b/passes/tests/test_abcloop.cc
@@ -171,7 +171,7 @@ static void test_abcloop()
}
log("Found viable UUT after %d cycles:\n", create_cycles);
- Pass::call(design, "write_ilang");
+ Pass::call(design, "write_rtlil");
Pass::call(design, "abc");
log("\n");
diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc
index bdb475d3b..616981f32 100644
--- a/passes/tests/test_cell.cc
+++ b/passes/tests/test_cell.cc
@@ -264,6 +264,10 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
cell->setPort(ID::Y, wire);
}
+ if (cell_type.in(ID($shiftx))) {
+ cell->parameters[ID::A_SIGNED] = false;
+ }
+
if (cell_type.in(ID($shl), ID($shr), ID($sshl), ID($sshr))) {
cell->parameters[ID::B_SIGNED] = false;
}
@@ -674,12 +678,12 @@ struct TestCellPass : public Pass {
log(" -s {positive_integer}\n");
log(" use this value as rng seed value (default = unix time).\n");
log("\n");
- log(" -f {ilang_file}\n");
- log(" don't generate circuits. instead load the specified ilang file.\n");
+ log(" -f {rtlil_file}\n");
+ log(" don't generate circuits. instead load the specified RTLIL file.\n");
log("\n");
log(" -w {filename_prefix}\n");
log(" don't test anything. just generate the circuits and write them\n");
- log(" to ilang files with the specified prefix\n");
+ log(" to RTLIL files with the specified prefix\n");
log("\n");
log(" -map {filename}\n");
log(" pass this option to techmap.\n");
@@ -720,7 +724,7 @@ struct TestCellPass : public Pass {
{
int num_iter = 100;
std::string techmap_cmd = "techmap -assert";
- std::string ilang_file, write_prefix;
+ std::string rtlil_file, write_prefix;
xorshift32_state = 0;
std::ofstream vlog_file;
bool muxdiv = false;
@@ -746,7 +750,7 @@ struct TestCellPass : public Pass {
continue;
}
if (args[argidx] == "-f" && argidx+1 < GetSize(args)) {
- ilang_file = args[++argidx];
+ rtlil_file = args[++argidx];
num_iter = 1;
continue;
}
@@ -906,10 +910,10 @@ struct TestCellPass : public Pass {
selected_cell_types.push_back(args[argidx]);
}
- if (!ilang_file.empty()) {
+ if (!rtlil_file.empty()) {
if (!selected_cell_types.empty())
log_cmd_error("Do not specify any cell types when using -f.\n");
- selected_cell_types.push_back(ID(ilang));
+ selected_cell_types.push_back(ID(rtlil));
}
if (selected_cell_types.empty())
@@ -921,12 +925,12 @@ struct TestCellPass : public Pass {
for (int i = 0; i < num_iter; i++)
{
RTLIL::Design *design = new RTLIL::Design;
- if (cell_type == ID(ilang))
- Frontend::frontend_call(design, NULL, std::string(), "ilang " + ilang_file);
+ if (cell_type == ID(rtlil))
+ Frontend::frontend_call(design, NULL, std::string(), "rtlil " + rtlil_file);
else
create_gold_module(design, cell_type, cell_types.at(cell_type), constmode, muxdiv);
if (!write_prefix.empty()) {
- Pass::call(design, stringf("write_ilang %s_%s_%05d.il", write_prefix.c_str(), cell_type.c_str()+1, i));
+ Pass::call(design, stringf("write_rtlil %s_%s_%05d.il", write_prefix.c_str(), cell_type.c_str()+1, i));
} else if (edges) {
Pass::call(design, "dump gold");
run_edges_test(design, verbose);