aboutsummaryrefslogtreecommitdiffstats
path: root/passes/proc
diff options
context:
space:
mode:
Diffstat (limited to 'passes/proc')
-rw-r--r--passes/proc/Makefile.inc1
-rw-r--r--passes/proc/proc.cc11
-rw-r--r--passes/proc/proc_dff.cc39
-rw-r--r--passes/proc/proc_rom.cc252
4 files changed, 271 insertions, 32 deletions
diff --git a/passes/proc/Makefile.inc b/passes/proc/Makefile.inc
index 50244bf33..21e169a34 100644
--- a/passes/proc/Makefile.inc
+++ b/passes/proc/Makefile.inc
@@ -5,6 +5,7 @@ OBJS += passes/proc/proc_clean.o
OBJS += passes/proc/proc_rmdead.o
OBJS += passes/proc/proc_init.o
OBJS += passes/proc/proc_arst.o
+OBJS += passes/proc/proc_rom.o
OBJS += passes/proc/proc_mux.o
OBJS += passes/proc/proc_dlatch.o
OBJS += passes/proc/proc_dff.o
diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc
index d7aac57b6..c18651d5e 100644
--- a/passes/proc/proc.cc
+++ b/passes/proc/proc.cc
@@ -40,6 +40,7 @@ struct ProcPass : public Pass {
log(" proc_prune\n");
log(" proc_init\n");
log(" proc_arst\n");
+ log(" proc_rom\n");
log(" proc_mux\n");
log(" proc_dlatch\n");
log(" proc_dff\n");
@@ -55,6 +56,9 @@ struct ProcPass : public Pass {
log(" -nomux\n");
log(" Will omit the proc_mux pass.\n");
log("\n");
+ log(" -norom\n");
+ log(" Will omit the proc_rom pass.\n");
+ log("\n");
log(" -global_arst [!]<netname>\n");
log(" This option is passed through to proc_arst.\n");
log("\n");
@@ -72,6 +76,7 @@ struct ProcPass : public Pass {
bool ifxmode = false;
bool nomux = false;
bool noopt = false;
+ bool norom = false;
log_header(design, "Executing PROC pass (convert processes to netlists).\n");
log_push();
@@ -95,6 +100,10 @@ struct ProcPass : public Pass {
noopt = true;
continue;
}
+ if (args[argidx] == "-norom") {
+ norom = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -108,6 +117,8 @@ struct ProcPass : public Pass {
Pass::call(design, "proc_arst");
else
Pass::call(design, "proc_arst -global_arst " + global_arst);
+ if (!norom)
+ Pass::call(design, "proc_rom");
if (!nomux)
Pass::call(design, ifxmode ? "proc_mux -ifx" : "proc_mux");
Pass::call(design, "proc_dlatch");
diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc
index da2a14c82..234671df5 100644
--- a/passes/proc/proc_dff.cc
+++ b/passes/proc/proc_dff.cc
@@ -143,48 +143,23 @@ void gen_dffsr_complex(RTLIL::Module *mod, RTLIL::SigSpec sig_d, RTLIL::SigSpec
cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative");
}
-void gen_dffsr(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_out,
+void gen_aldff(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_out,
bool clk_polarity, bool set_polarity, RTLIL::SigSpec clk, RTLIL::SigSpec set, RTLIL::Process *proc)
{
std::stringstream sstr;
sstr << "$procdff$" << (autoidx++);
- RTLIL::SigSpec sig_set_inv = mod->addWire(NEW_ID, sig_in.size());
- RTLIL::SigSpec sig_sr_set = mod->addWire(NEW_ID, sig_in.size());
- RTLIL::SigSpec sig_sr_clr = mod->addWire(NEW_ID, sig_in.size());
-
- RTLIL::Cell *inv_set = mod->addCell(NEW_ID, ID($not));
- inv_set->parameters[ID::A_SIGNED] = RTLIL::Const(0);
- inv_set->parameters[ID::A_WIDTH] = RTLIL::Const(sig_in.size());
- inv_set->parameters[ID::Y_WIDTH] = RTLIL::Const(sig_in.size());
- inv_set->setPort(ID::A, sig_set);
- inv_set->setPort(ID::Y, sig_set_inv);
-
- RTLIL::Cell *mux_sr_set = mod->addCell(NEW_ID, ID($mux));
- mux_sr_set->parameters[ID::WIDTH] = RTLIL::Const(sig_in.size());
- mux_sr_set->setPort(set_polarity ? ID::A : ID::B, RTLIL::Const(0, sig_in.size()));
- mux_sr_set->setPort(set_polarity ? ID::B : ID::A, sig_set);
- mux_sr_set->setPort(ID::Y, sig_sr_set);
- mux_sr_set->setPort(ID::S, set);
-
- RTLIL::Cell *mux_sr_clr = mod->addCell(NEW_ID, ID($mux));
- mux_sr_clr->parameters[ID::WIDTH] = RTLIL::Const(sig_in.size());
- mux_sr_clr->setPort(set_polarity ? ID::A : ID::B, RTLIL::Const(0, sig_in.size()));
- mux_sr_clr->setPort(set_polarity ? ID::B : ID::A, sig_set_inv);
- mux_sr_clr->setPort(ID::Y, sig_sr_clr);
- mux_sr_clr->setPort(ID::S, set);
-
- RTLIL::Cell *cell = mod->addCell(sstr.str(), ID($dffsr));
+ RTLIL::Cell *cell = mod->addCell(sstr.str(), ID($aldff));
cell->attributes = proc->attributes;
+
cell->parameters[ID::WIDTH] = RTLIL::Const(sig_in.size());
+ cell->parameters[ID::ALOAD_POLARITY] = RTLIL::Const(set_polarity, 1);
cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity, 1);
- cell->parameters[ID::SET_POLARITY] = RTLIL::Const(true, 1);
- cell->parameters[ID::CLR_POLARITY] = RTLIL::Const(true, 1);
cell->setPort(ID::D, sig_in);
cell->setPort(ID::Q, sig_out);
+ cell->setPort(ID::AD, sig_set);
cell->setPort(ID::CLK, clk);
- cell->setPort(ID::SET, sig_sr_set);
- cell->setPort(ID::CLR, sig_sr_clr);
+ cell->setPort(ID::ALOAD, set);
log(" created %s cell `%s' with %s edge clock and %s level non-const reset.\n", cell->type.c_str(), cell->name.c_str(),
clk_polarity ? "positive" : "negative", set_polarity ? "positive" : "negative");
@@ -355,7 +330,7 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce)
else if (!rstval.is_fully_const() && !ce.eval(rstval))
{
log_warning("Async reset value `%s' is not constant!\n", log_signal(rstval));
- gen_dffsr(mod, insig, rstval, sig_q,
+ gen_aldff(mod, insig, rstval, sig_q,
sync_edge->type == RTLIL::SyncType::STp,
sync_level && sync_level->type == RTLIL::SyncType::ST1,
sync_edge->signal, sync_level->signal, proc);
diff --git a/passes/proc/proc_rom.cc b/passes/proc/proc_rom.cc
new file mode 100644
index 000000000..b83466ce7
--- /dev/null
+++ b/passes/proc/proc_rom.cc
@@ -0,0 +1,252 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2022 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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include "kernel/mem.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct RomWorker
+{
+ RTLIL::Module *module;
+ SigMap sigmap;
+
+ int count = 0;
+
+ RomWorker(RTLIL::Module *mod) : module(mod), sigmap(mod) {}
+
+ void do_switch(RTLIL::SwitchRule *sw)
+ {
+ for (auto cs : sw->cases) {
+ do_case(cs);
+ }
+
+ if (sw->cases.empty()) {
+ log_debug("rejecting switch: no cases\n");
+ return;
+ }
+
+ // A switch can be converted into ROM when:
+ //
+ // 1. No case contains a nested switch
+ // 2. All cases have the same set of assigned signals
+ // 3. All right-hand values in cases are constants
+ // 4. All compare values used in cases are fully-defined constants
+ // 5. The cases must cover all possible values (possibly by using default case)
+
+ SigSpec lhs;
+ dict<SigBit, int> lhs_lookup;
+ for (auto &it: sw->cases[0]->actions) {
+ for (auto bit: it.first) {
+ if (!lhs_lookup.count(bit)) {
+ lhs_lookup[bit] = GetSize(lhs);
+ lhs.append(bit);
+ }
+ }
+ }
+
+ int swsigbits = 0;
+ for (int i = 0; i < GetSize(sw->signal); i++)
+ if (sw->signal[i] != State::S0)
+ swsigbits = i + 1;
+
+ dict<int, Const> vals;
+ Const default_val;
+ bool got_default = false;
+ int maxaddr = 0;
+ for (auto cs : sw->cases) {
+ if (!cs->switches.empty()) {
+ log_debug("rejecting switch: has nested switches\n");
+ return;
+ }
+ Const val = Const(State::Sm, GetSize(lhs));
+ for (auto &it: cs->actions) {
+ if (!it.second.is_fully_const()) {
+ log_debug("rejecting switch: rhs not const\n");
+ return;
+ }
+ for (int i = 0; i < GetSize(it.first); i++) {
+ auto it2 = lhs_lookup.find(it.first[i]);
+ if (it2 == lhs_lookup.end()) {
+ log_debug("rejecting switch: lhs not uniform\n");
+ return;
+ }
+ val[it2->second] = it.second[i].data;
+ }
+ }
+ for (auto bit: val.bits) {
+ if (bit == State::Sm) {
+ log_debug("rejecting switch: lhs not uniform\n");
+ return;
+ }
+ }
+
+ for (auto &addr: cs->compare) {
+ if (!addr.is_fully_def()) {
+ log_debug("rejecting switch: case value has undef bits\n");
+ return;
+ }
+ Const c = addr.as_const();
+ while (GetSize(c) && c.bits.back() == State::S0)
+ c.bits.pop_back();
+ if (GetSize(c) > swsigbits)
+ continue;
+ if (GetSize(c) > 30) {
+ log_debug("rejecting switch: address too large\n");
+ return;
+ }
+ int a = c.as_int();
+ if (vals.count(a))
+ continue;
+ vals[a] = val;
+ if (a > maxaddr)
+ maxaddr = a;
+ }
+ if (cs->compare.empty()) {
+ default_val = val;
+ got_default = true;
+ break;
+ }
+ }
+ int abits = ceil_log2(maxaddr + 1);
+ if (!got_default && (swsigbits > 30 || GetSize(vals) != (1 << swsigbits))) {
+ log_debug("rejecting switch: not all values are covered\n");
+ return;
+ }
+
+ // TODO: better density heuristic?
+ if (GetSize(vals) < 8) {
+ log_debug("rejecting switch: not enough values\n");
+ return;
+ }
+ if ((1 << abits) / GetSize(vals) > 4) {
+ log_debug("rejecting switch: not enough density\n");
+ return;
+ }
+
+ // Ok, let's do it.
+ SigSpec rdata = module->addWire(NEW_ID, GetSize(lhs));
+ Mem mem(module, NEW_ID, GetSize(lhs), 0, 1 << abits);
+ mem.attributes = sw->attributes;
+
+ Const init_data;
+ for (int i = 0; i < mem.size; i++) {
+ auto it = vals.find(i);
+ if (it == vals.end()) {
+ log_assert(got_default);
+ for (auto bit: default_val.bits)
+ init_data.bits.push_back(bit);
+ } else {
+ for (auto bit: it->second.bits)
+ init_data.bits.push_back(bit);
+ }
+ }
+
+ MemInit init;
+ init.addr = 0;
+ init.data = init_data;
+ init.en = Const(State::S1, GetSize(lhs));
+ mem.inits.push_back(std::move(init));
+
+ MemRd rd;
+ rd.addr = sw->signal.extract(0, abits);
+ rd.data = rdata;
+ rd.init_value = Const(State::Sx, GetSize(lhs));
+ rd.arst_value = Const(State::Sx, GetSize(lhs));
+ rd.srst_value = Const(State::Sx, GetSize(lhs));
+ mem.rd_ports.push_back(std::move(rd));
+
+ mem.emit();
+ for (auto cs: sw->cases)
+ delete cs;
+ sw->cases.clear();
+ sw->signal = sw->signal.extract(0, swsigbits);
+ if (abits == GetSize(sw->signal)) {
+ sw->signal = SigSpec();
+ RTLIL::CaseRule *cs = new RTLIL::CaseRule;
+ cs->actions.push_back(SigSig(lhs, rdata));
+ sw->cases.push_back(cs);
+ } else {
+ sw->signal = sw->signal.extract_end(abits);
+ RTLIL::CaseRule *cs = new RTLIL::CaseRule;
+ cs->compare.push_back(Const(State::S0, GetSize(sw->signal)));
+ cs->actions.push_back(SigSig(lhs, rdata));
+ sw->cases.push_back(cs);
+ RTLIL::CaseRule *cs2 = new RTLIL::CaseRule;
+ cs2->actions.push_back(SigSig(lhs, default_val));
+ sw->cases.push_back(cs2);
+ }
+
+ count += 1;
+ }
+
+ void do_case(RTLIL::CaseRule *cs)
+ {
+ for (auto sw: cs->switches) {
+ do_switch(sw);
+ }
+ }
+
+ void do_process(RTLIL::Process *pr)
+ {
+ do_case(&pr->root_case);
+ }
+};
+
+struct ProcRomPass : public Pass {
+ ProcRomPass() : Pass("proc_rom", "convert switches to ROMs") { }
+ void help() override
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" proc_rom [selection]\n");
+ log("\n");
+ log("This pass converts switches into read-only memories when appropriate.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
+ {
+ int total_count = 0;
+ log_header(design, "Executing PROC_ROM pass (convert switches to ROMs).\n");
+
+ extra_args(args, 1, design);
+
+ for (auto mod : design->modules()) {
+ if (!design->selected(mod))
+ continue;
+ RomWorker worker(mod);
+ for (auto &proc_it : mod->processes) {
+ if (!design->selected(mod, proc_it.second))
+ continue;
+ worker.do_process(proc_it.second);
+ }
+ total_count += worker.count;
+ }
+
+ log("Converted %d switch%s.\n",
+ total_count, total_count == 1 ? "" : "es");
+ }
+} ProcRomPass;
+
+PRIVATE_NAMESPACE_END