aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/calc.cc51
-rw-r--r--kernel/celledges.cc40
-rw-r--r--kernel/celltypes.h29
-rw-r--r--kernel/consteval.h35
-rw-r--r--kernel/fstdata.cc219
-rw-r--r--kernel/fstdata.h83
-rw-r--r--kernel/mem.cc312
-rw-r--r--kernel/mem.h30
-rw-r--r--kernel/qcsat.cc2
-rw-r--r--kernel/rtlil.cc52
-rw-r--r--kernel/rtlil.h7
-rw-r--r--kernel/satgen.cc100
-rw-r--r--kernel/yosys.cc17
-rw-r--r--kernel/yosys.h3
14 files changed, 960 insertions, 20 deletions
diff --git a/kernel/calc.cc b/kernel/calc.cc
index 1e6410f7d..0865db526 100644
--- a/kernel/calc.cc
+++ b/kernel/calc.cc
@@ -609,5 +609,56 @@ RTLIL::Const RTLIL::const_neg(const RTLIL::Const &arg1, const RTLIL::Const&, boo
return RTLIL::const_sub(zero, arg1_ext, true, signed1, result_len);
}
+RTLIL::Const RTLIL::const_bmux(const RTLIL::Const &arg1, const RTLIL::Const &arg2)
+{
+ std::vector<RTLIL::State> t = arg1.bits;
+
+ for (int i = GetSize(arg2)-1; i >= 0; i--)
+ {
+ RTLIL::State sel = arg2.bits.at(i);
+ std::vector<RTLIL::State> new_t;
+ if (sel == State::S0)
+ new_t = std::vector<RTLIL::State>(t.begin(), t.begin() + GetSize(t)/2);
+ else if (sel == State::S1)
+ new_t = std::vector<RTLIL::State>(t.begin() + GetSize(t)/2, t.end());
+ else
+ for (int j = 0; j < GetSize(t)/2; j++)
+ new_t.push_back(t[j] == t[j + GetSize(t)/2] ? t[j] : RTLIL::Sx);
+ t.swap(new_t);
+ }
+
+ return t;
+}
+
+RTLIL::Const RTLIL::const_demux(const RTLIL::Const &arg1, const RTLIL::Const &arg2)
+{
+ int width = GetSize(arg1);
+ int s_width = GetSize(arg2);
+ std::vector<RTLIL::State> res;
+ for (int i = 0; i < (1 << s_width); i++)
+ {
+ bool ne = false;
+ bool x = false;
+ for (int j = 0; j < s_width; j++) {
+ bool bit = i & 1 << j;
+ if (arg2[j] == (bit ? RTLIL::S0 : RTLIL::S1))
+ ne = true;
+ else if (arg2[j] != RTLIL::S0 && arg2[j] != RTLIL::S1)
+ x = true;
+ }
+ if (ne) {
+ for (int j = 0; j < width; j++)
+ res.push_back(State::S0);
+ } else if (x) {
+ for (int j = 0; j < width; j++)
+ res.push_back(arg1.bits[j] == State::S0 ? State::S0 : State::Sx);
+ } else {
+ for (int j = 0; j < width; j++)
+ res.push_back(arg1.bits[j]);
+ }
+ }
+ return res;
+}
+
YOSYS_NAMESPACE_END
diff --git a/kernel/celledges.cc b/kernel/celledges.cc
index af07d26b3..c43ba8db3 100644
--- a/kernel/celledges.cc
+++ b/kernel/celledges.cc
@@ -142,6 +142,36 @@ void mux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
}
}
+void bmux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
+{
+ int width = GetSize(cell->getPort(ID::Y));
+ int a_width = GetSize(cell->getPort(ID::A));
+ int s_width = GetSize(cell->getPort(ID::S));
+
+ for (int i = 0; i < width; i++)
+ {
+ for (int k = i; k < a_width; k += width)
+ db->add_edge(cell, ID::A, k, ID::Y, i, -1);
+
+ for (int k = 0; k < s_width; k++)
+ db->add_edge(cell, ID::S, k, ID::Y, i, -1);
+ }
+}
+
+void demux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
+{
+ int width = GetSize(cell->getPort(ID::Y));
+ int a_width = GetSize(cell->getPort(ID::A));
+ int s_width = GetSize(cell->getPort(ID::S));
+
+ for (int i = 0; i < width; i++)
+ {
+ db->add_edge(cell, ID::A, i % a_width, ID::Y, i, -1);
+ for (int k = 0; k < s_width; k++)
+ db->add_edge(cell, ID::S, k, ID::Y, i, -1);
+ }
+}
+
PRIVATE_NAMESPACE_END
bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell)
@@ -187,6 +217,16 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL
return true;
}
+ if (cell->type == ID($bmux)) {
+ bmux_op(this, cell);
+ return true;
+ }
+
+ if (cell->type == ID($demux)) {
+ demux_op(this, cell);
+ return true;
+ }
+
// FIXME: $mul $div $mod $divfloor $modfloor $slice $concat
// FIXME: $lut $sop $alu $lcu $macc $fa
diff --git a/kernel/celltypes.h b/kernel/celltypes.h
index 879ac0edc..7e9cfb38d 100644
--- a/kernel/celltypes.h
+++ b/kernel/celltypes.h
@@ -127,6 +127,9 @@ struct CellTypes
for (auto type : std::vector<RTLIL::IdString>({ID($mux), ID($pmux)}))
setup_type(type, {ID::A, ID::B, ID::S}, {ID::Y}, true);
+ for (auto type : std::vector<RTLIL::IdString>({ID($bmux), ID($demux)}))
+ setup_type(type, {ID::A, ID::S}, {ID::Y}, true);
+
setup_type(ID($lcu), {ID::P, ID::G, ID::CI}, {ID::CO}, true);
setup_type(ID($alu), {ID::A, ID::B, ID::CI, ID::BI}, {ID::X, ID::Y, ID::CO}, true);
setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, true);
@@ -411,6 +414,16 @@ struct CellTypes
return ret;
}
+ if (cell->type == ID($bmux))
+ {
+ return const_bmux(arg1, arg2);
+ }
+
+ if (cell->type == ID($demux))
+ {
+ return const_demux(arg1, arg2);
+ }
+
if (cell->type == ID($lut))
{
int width = cell->parameters.at(ID::WIDTH).as_int();
@@ -420,21 +433,7 @@ struct CellTypes
t.push_back(State::S0);
t.resize(1 << width);
- for (int i = width-1; i >= 0; i--) {
- RTLIL::State sel = arg1.bits.at(i);
- std::vector<RTLIL::State> new_t;
- if (sel == State::S0)
- new_t = std::vector<RTLIL::State>(t.begin(), t.begin() + GetSize(t)/2);
- else if (sel == State::S1)
- new_t = std::vector<RTLIL::State>(t.begin() + GetSize(t)/2, t.end());
- else
- for (int j = 0; j < GetSize(t)/2; j++)
- new_t.push_back(t[j] == t[j + GetSize(t)/2] ? t[j] : RTLIL::Sx);
- t.swap(new_t);
- }
-
- log_assert(GetSize(t) == 1);
- return t;
+ return const_bmux(t, arg1);
}
if (cell->type == ID($sop))
diff --git a/kernel/consteval.h b/kernel/consteval.h
index 3edfc490c..642eb42b2 100644
--- a/kernel/consteval.h
+++ b/kernel/consteval.h
@@ -135,8 +135,6 @@ struct ConstEval
if (cell->hasPort(ID::S)) {
sig_s = cell->getPort(ID::S);
- if (!eval(sig_s, undef, cell))
- return false;
}
if (cell->hasPort(ID::A))
@@ -151,6 +149,9 @@ struct ConstEval
int count_maybe_set_s_bits = 0;
int count_set_s_bits = 0;
+ if (!eval(sig_s, undef, cell))
+ return false;
+
for (int i = 0; i < sig_s.size(); i++)
{
RTLIL::State s_bit = sig_s.extract(i, 1).as_const().bits.at(0);
@@ -198,6 +199,36 @@ struct ConstEval
else
set(sig_y, y_values.front());
}
+ else if (cell->type == ID($bmux))
+ {
+ if (!eval(sig_s, undef, cell))
+ return false;
+
+ if (sig_s.is_fully_def()) {
+ int sel = sig_s.as_int();
+ int width = GetSize(sig_y);
+ SigSpec res = sig_a.extract(sel * width, width);
+ if (!eval(res, undef, cell))
+ return false;
+ set(sig_y, res.as_const());
+ } else {
+ if (!eval(sig_a, undef, cell))
+ return false;
+ set(sig_y, const_bmux(sig_a.as_const(), sig_s.as_const()));
+ }
+ }
+ else if (cell->type == ID($demux))
+ {
+ if (!eval(sig_a, undef, cell))
+ return false;
+ if (sig_a.is_fully_zero()) {
+ set(sig_y, Const(0, GetSize(sig_y)));
+ } else {
+ if (!eval(sig_s, undef, cell))
+ return false;
+ set(sig_y, const_demux(sig_a.as_const(), sig_s.as_const()));
+ }
+ }
else if (cell->type == ID($fa))
{
RTLIL::SigSpec sig_c = cell->getPort(ID::C);
diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc
new file mode 100644
index 000000000..6cf28625c
--- /dev/null
+++ b/kernel/fstdata.cc
@@ -0,0 +1,219 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2022 Miodrag Milanovic <micko@yosyshq.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/fstdata.h"
+
+USING_YOSYS_NAMESPACE
+
+
+static std::string file_base_name(std::string const & path)
+{
+ return path.substr(path.find_last_of("/\\") + 1);
+}
+
+FstData::FstData(std::string filename) : ctx(nullptr)
+{
+ #if !defined(YOSYS_DISABLE_SPAWN)
+ std::string filename_trim = file_base_name(filename);
+ if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".vcd") == 0) {
+ filename_trim.erase(filename_trim.size()-4);
+ tmp_file = stringf("/tmp/converted_%s.fst", filename_trim.c_str());
+ std::string cmd = stringf("vcd2fst %s %s", filename.c_str(), tmp_file.c_str());
+ log("Exec: %s\n", cmd.c_str());
+ if (run_command(cmd) != 0)
+ log_cmd_error("Shell command failed!\n");
+ filename = tmp_file;
+ }
+ #endif
+ const std::vector<std::string> g_units = { "s", "ms", "us", "ns", "ps", "fs", "as", "zs" };
+ ctx = (fstReaderContext *)fstReaderOpen(filename.c_str());
+ if (!ctx)
+ log_error("Error opening '%s' as FST file\n", filename.c_str());
+ int scale = (int)fstReaderGetTimescale(ctx);
+ timescale = pow(10.0, scale);
+ timescale_str = "";
+ int unit = 0;
+ int zeros = 0;
+ if (scale > 0) {
+ zeros = scale;
+ } else {
+ if ((scale % 3) == 0) {
+ zeros = (-scale % 3);
+ unit = (-scale / 3);
+ } else {
+ zeros = 3 - (-scale % 3);
+ unit = (-scale / 3) + 1;
+ }
+ }
+ for (int i=0;i<zeros; i++) timescale_str += "0";
+ timescale_str += g_units[unit];
+ extractVarNames();
+}
+
+FstData::~FstData()
+{
+ if (ctx)
+ fstReaderClose(ctx);
+ if (!tmp_file.empty())
+ remove(tmp_file.c_str());
+}
+
+uint64_t FstData::getStartTime() { return fstReaderGetStartTime(ctx); }
+
+uint64_t FstData::getEndTime() { return fstReaderGetEndTime(ctx); }
+
+fstHandle FstData::getHandle(std::string name) {
+ if (name_to_handle.find(name) != name_to_handle.end())
+ return name_to_handle[name];
+ else
+ return 0;
+};
+
+static std::string remove_spaces(std::string str)
+{
+ str.erase(std::remove(str.begin(), str.end(), ' '), str.end());
+ return str;
+}
+
+void FstData::extractVarNames()
+{
+ struct fstHier *h;
+ intptr_t snum = 0;
+
+ while ((h = fstReaderIterateHier(ctx))) {
+ switch (h->htyp) {
+ case FST_HT_SCOPE: {
+ snum++;
+ std::string fst_scope_name = fstReaderPushScope(ctx, h->u.scope.name, (void *)(snum));
+ if (h->u.scope.typ == FST_ST_VCD_MODULE)
+ scopes.push_back(fst_scope_name);
+ break;
+ }
+ case FST_HT_UPSCOPE: {
+ fstReaderPopScope(ctx);
+ snum = fstReaderGetCurrentScopeLen(ctx) ? (intptr_t)fstReaderGetCurrentScopeUserInfo(ctx) : 0;
+ break;
+ }
+ case FST_HT_VAR: {
+ FstVar var;
+ var.id = h->u.var.handle;
+ var.is_alias = h->u.var.is_alias;
+ var.is_reg = (fstVarType)h->u.var.typ == FST_VT_VCD_REG;
+ var.name = remove_spaces(h->u.var.name);
+ var.scope = scopes.back();
+ var.width = h->u.var.length;
+ vars.push_back(var);
+ if (!var.is_alias)
+ handle_to_var[h->u.var.handle] = var;
+ std::string clean_name;
+ for(size_t i=0;i<strlen(h->u.var.name);i++)
+ {
+ char c = h->u.var.name[i];
+ if(c==' ') break;
+ clean_name += c;
+ }
+ if (clean_name[0]=='\\')
+ clean_name = clean_name.substr(1);
+
+ name_to_handle[var.scope+"."+clean_name] = h->u.var.handle;
+ break;
+ }
+ }
+ }
+}
+
+
+static void reconstruct_clb_varlen_attimes(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen)
+{
+ FstData *ptr = (FstData*)user_data;
+ ptr->reconstruct_callback_attimes(pnt_time, pnt_facidx, pnt_value, plen);
+}
+
+static void reconstruct_clb_attimes(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value)
+{
+ FstData *ptr = (FstData*)user_data;
+ uint32_t plen = (pnt_value) ? strlen((const char *)pnt_value) : 0;
+ ptr->reconstruct_callback_attimes(pnt_time, pnt_facidx, pnt_value, plen);
+}
+
+void FstData::reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t /* plen */)
+{
+ if (pnt_time > end_time) return;
+ // if we are past the timestamp
+ bool is_clock = false;
+ if (!all_samples) {
+ for(auto &s : clk_signals) {
+ if (s==pnt_facidx) {
+ is_clock=true;
+ break;
+ }
+ }
+ }
+
+ if (pnt_time > past_time) {
+ past_data = last_data;
+ past_time = pnt_time;
+ }
+
+ if (pnt_time > last_time) {
+ if (all_samples) {
+ callback(last_time);
+ last_time = pnt_time;
+ } else {
+ if (is_clock) {
+ std::string val = std::string((const char *)pnt_value);
+ std::string prev = past_data[pnt_facidx];
+ if ((prev!="1" && val=="1") || (prev!="0" && val=="0")) {
+ callback(last_time);
+ last_time = pnt_time;
+ }
+ }
+ }
+ }
+ // always update last_data
+ last_data[pnt_facidx] = std::string((const char *)pnt_value);
+}
+
+void FstData::reconstructAllAtTimes(std::vector<fstHandle> &signal, uint64_t start, uint64_t end, CallbackFunction cb)
+{
+ clk_signals = signal;
+ callback = cb;
+ start_time = start;
+ end_time = end;
+ last_data.clear();
+ last_time = start_time;
+ past_data.clear();
+ past_time = start_time;
+ all_samples = clk_signals.empty();
+
+ fstReaderSetUnlimitedTimeRange(ctx);
+ fstReaderSetFacProcessMaskAll(ctx);
+ fstReaderIterBlocks2(ctx, reconstruct_clb_attimes, reconstruct_clb_varlen_attimes, this, nullptr);
+ past_data = last_data;
+ callback(last_time);
+ if (last_time!=end_time)
+ callback(end_time);
+}
+
+std::string FstData::valueOf(fstHandle signal)
+{
+ if (past_data.find(signal) == past_data.end())
+ log_error("Signal id %d not found\n", (int)signal);
+ return past_data[signal];
+}
diff --git a/kernel/fstdata.h b/kernel/fstdata.h
new file mode 100644
index 000000000..0e9d27e2c
--- /dev/null
+++ b/kernel/fstdata.h
@@ -0,0 +1,83 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2022 Miodrag Milanovic <micko@yosyshq.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef FSTDATA_H
+#define FSTDATA_H
+
+#include "kernel/yosys.h"
+#include "libs/fst/fstapi.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+typedef std::function<void(uint64_t)> CallbackFunction;
+struct fst_end_of_data_exception { };
+
+struct FstVar
+{
+ fstHandle id;
+ std::string name;
+ bool is_alias;
+ bool is_reg;
+ std::string scope;
+ int width;
+};
+
+class FstData
+{
+ public:
+ FstData(std::string filename);
+ ~FstData();
+
+ uint64_t getStartTime();
+ uint64_t getEndTime();
+
+ std::vector<FstVar>& getVars() { return vars; };
+
+ void reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen);
+ void reconstructAllAtTimes(std::vector<fstHandle> &signal, uint64_t start_time, uint64_t end_time, CallbackFunction cb);
+
+ std::string valueOf(fstHandle signal);
+ fstHandle getHandle(std::string name);
+ double getTimescale() { return timescale; }
+ const char *getTimescaleString() { return timescale_str.c_str(); }
+private:
+ void extractVarNames();
+
+ struct fstReaderContext *ctx;
+ std::vector<std::string> scopes;
+ std::vector<FstVar> vars;
+ std::map<fstHandle, FstVar> handle_to_var;
+ std::map<std::string, fstHandle> name_to_handle;
+ std::map<fstHandle, std::string> last_data;
+ uint64_t last_time;
+ std::map<fstHandle, std::string> past_data;
+ uint64_t past_time;
+ double timescale;
+ std::string timescale_str;
+ uint64_t start_time;
+ uint64_t end_time;
+ CallbackFunction callback;
+ std::vector<fstHandle> clk_signals;
+ bool all_samples;
+ std::string tmp_file;
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/mem.cc b/kernel/mem.cc
index 96168ff76..059f8f934 100644
--- a/kernel/mem.cc
+++ b/kernel/mem.cc
@@ -1352,3 +1352,315 @@ void Mem::widen_wr_port(int idx, int wide_log2) {
port.wide_log2 = wide_log2;
}
}
+
+void Mem::emulate_rden(int idx, FfInitVals *initvals) {
+ auto &port = rd_ports[idx];
+ log_assert(port.clk_enable);
+ emulate_rd_ce_over_srst(idx);
+ Wire *new_data = module->addWire(NEW_ID, GetSize(port.data));
+ Wire *prev_data = module->addWire(NEW_ID, GetSize(port.data));
+ Wire *sel = module->addWire(NEW_ID);
+ FfData ff_sel(module, initvals, NEW_ID);
+ FfData ff_data(module, initvals, NEW_ID);
+ ff_sel.width = 1;
+ ff_sel.has_clk = true;
+ ff_sel.sig_clk = port.clk;
+ ff_sel.pol_clk = port.clk_polarity;
+ ff_sel.sig_d = port.en;
+ ff_sel.sig_q = sel;
+ ff_data.width = GetSize(port.data);
+ ff_data.has_clk = true;
+ ff_data.sig_clk = port.clk;
+ ff_data.pol_clk = port.clk_polarity;
+ ff_data.sig_d = port.data;
+ ff_data.sig_q = prev_data;
+ if (!port.init_value.is_fully_undef()) {
+ ff_sel.val_init = State::S0;
+ ff_data.val_init = port.init_value;
+ port.init_value = Const(State::Sx, GetSize(port.data));
+ } else {
+ ff_sel.val_init = State::Sx;
+ ff_data.val_init = Const(State::Sx, GetSize(port.data));
+ }
+ if (port.arst != State::S0) {
+ ff_sel.has_arst = true;
+ ff_sel.val_arst = State::S0;
+ ff_sel.sig_arst = port.arst;
+ ff_sel.pol_arst = true;
+ ff_data.has_arst = true;
+ ff_data.val_arst = port.arst_value;
+ ff_data.sig_arst = port.arst;
+ ff_data.pol_arst = true;
+ port.arst = State::S0;
+ }
+ if (port.srst != State::S0) {
+ log_assert(!port.ce_over_srst);
+ ff_sel.has_srst = true;
+ ff_sel.val_srst = State::S0;
+ ff_sel.sig_srst = port.srst;
+ ff_sel.pol_srst = true;
+ ff_sel.ce_over_srst = false;
+ ff_data.has_srst = true;
+ ff_data.val_srst = port.srst_value;
+ ff_data.sig_srst = port.srst;
+ ff_data.pol_srst = true;
+ ff_data.ce_over_srst = false;
+ port.srst = State::S0;
+ }
+ ff_sel.emit();
+ ff_data.emit();
+ module->addMux(NEW_ID, prev_data, new_data, sel, port.data);
+ port.data = new_data;
+ port.en = State::S1;
+}
+
+void Mem::emulate_reset(int idx, bool emu_init, bool emu_arst, bool emu_srst, FfInitVals *initvals) {
+ auto &port = rd_ports[idx];
+ if (emu_init && !port.init_value.is_fully_undef()) {
+ Wire *sel = module->addWire(NEW_ID);
+ FfData ff_sel(module, initvals, NEW_ID);
+ Wire *new_data = module->addWire(NEW_ID, GetSize(port.data));
+ ff_sel.width = 1;
+ ff_sel.has_clk = true;
+ ff_sel.sig_clk = port.clk;
+ ff_sel.pol_clk = port.clk_polarity;
+ ff_sel.sig_d = State::S1;
+ ff_sel.sig_q = sel;
+ ff_sel.val_init = State::S0;
+ if (port.en != State::S1) {
+ ff_sel.has_ce = true;
+ ff_sel.sig_ce = port.en;
+ ff_sel.pol_ce = true;
+ ff_sel.ce_over_srst = port.ce_over_srst;
+ }
+ if (port.arst != State::S0) {
+ ff_sel.has_arst = true;
+ ff_sel.sig_arst = port.arst;
+ ff_sel.pol_arst = true;
+ if (emu_arst && port.arst_value == port.init_value) {
+ // If we're going to emulate async reset anyway, and the reset
+ // value is the same as init value, reuse the same mux.
+ ff_sel.val_arst = State::S0;
+ port.arst = State::S0;
+ } else {
+ ff_sel.val_arst = State::S1;
+ }
+ }
+ if (port.srst != State::S0) {
+ ff_sel.has_srst = true;
+ ff_sel.sig_srst = port.srst;
+ ff_sel.pol_srst = true;
+ if (emu_srst && port.srst_value == port.init_value) {
+ ff_sel.val_srst = State::S0;
+ port.srst = State::S0;
+ } else {
+ ff_sel.val_srst = State::S1;
+ }
+ }
+ ff_sel.emit();
+ module->addMux(NEW_ID, port.init_value, new_data, sel, port.data);
+ port.data = new_data;
+ port.init_value = Const(State::Sx, GetSize(port.data));
+ }
+ if (emu_arst && port.arst != State::S0) {
+ Wire *sel = module->addWire(NEW_ID);
+ FfData ff_sel(module, initvals, NEW_ID);
+ Wire *new_data = module->addWire(NEW_ID, GetSize(port.data));
+ ff_sel.width = 1;
+ ff_sel.has_clk = true;
+ ff_sel.sig_clk = port.clk;
+ ff_sel.pol_clk = port.clk_polarity;
+ ff_sel.sig_d = State::S1;
+ ff_sel.sig_q = sel;
+ if (port.init_value.is_fully_undef())
+ ff_sel.val_init = State::Sx;
+ else
+ ff_sel.val_init = State::S1;
+ if (port.en != State::S1) {
+ ff_sel.has_ce = true;
+ ff_sel.sig_ce = port.en;
+ ff_sel.pol_ce = true;
+ ff_sel.ce_over_srst = port.ce_over_srst;
+ }
+ ff_sel.has_arst = true;
+ ff_sel.sig_arst = port.arst;
+ ff_sel.pol_arst = true;
+ ff_sel.val_arst = State::S0;
+ if (port.srst != State::S0) {
+ ff_sel.has_srst = true;
+ ff_sel.sig_srst = port.srst;
+ ff_sel.pol_srst = true;
+ if (emu_srst && port.srst_value == port.arst_value) {
+ ff_sel.val_srst = State::S0;
+ port.srst = State::S0;
+ } else {
+ ff_sel.val_srst = State::S1;
+ }
+ }
+ ff_sel.emit();
+ module->addMux(NEW_ID, port.arst_value, new_data, sel, port.data);
+ port.data = new_data;
+ port.arst = State::S0;
+ }
+ if (emu_srst && port.srst != State::S0) {
+ Wire *sel = module->addWire(NEW_ID);
+ FfData ff_sel(module, initvals, NEW_ID);
+ Wire *new_data = module->addWire(NEW_ID, GetSize(port.data));
+ ff_sel.width = 1;
+ ff_sel.has_clk = true;
+ ff_sel.sig_clk = port.clk;
+ ff_sel.pol_clk = port.clk_polarity;
+ ff_sel.sig_d = State::S1;
+ ff_sel.sig_q = sel;
+ if (port.init_value.is_fully_undef())
+ ff_sel.val_init = State::Sx;
+ else
+ ff_sel.val_init = State::S1;
+ if (port.en != State::S1) {
+ ff_sel.has_ce = true;
+ ff_sel.sig_ce = port.en;
+ ff_sel.pol_ce = true;
+ ff_sel.ce_over_srst = port.ce_over_srst;
+ }
+ ff_sel.has_srst = true;
+ ff_sel.sig_srst = port.srst;
+ ff_sel.pol_srst = true;
+ ff_sel.val_srst = State::S0;
+ if (port.arst != State::S0) {
+ ff_sel.has_arst = true;
+ ff_sel.sig_arst = port.arst;
+ ff_sel.pol_arst = true;
+ ff_sel.val_arst = State::S1;
+ }
+ ff_sel.emit();
+ module->addMux(NEW_ID, port.srst_value, new_data, sel, port.data);
+ port.data = new_data;
+ port.srst = State::S0;
+ }
+}
+
+void Mem::emulate_rd_ce_over_srst(int idx) {
+ auto &port = rd_ports[idx];
+ log_assert(port.clk_enable);
+ if (port.en == State::S1 || port.srst == State::S0 || !port.ce_over_srst) {
+ port.ce_over_srst = false;
+ return;
+ }
+ port.ce_over_srst = false;
+ port.srst = module->And(NEW_ID, port.en, port.srst);
+}
+
+void Mem::emulate_rd_srst_over_ce(int idx) {
+ auto &port = rd_ports[idx];
+ log_assert(port.clk_enable);
+ if (port.en == State::S1 || port.srst == State::S0 || port.ce_over_srst) {
+ port.ce_over_srst = true;
+ return;
+ }
+ port.ce_over_srst = true;
+ port.en = module->Or(NEW_ID, port.en, port.srst);
+}
+
+bool Mem::emulate_read_first_ok() {
+ if (wr_ports.empty())
+ return false;
+ SigSpec clk = wr_ports[0].clk;
+ bool clk_polarity = wr_ports[0].clk_polarity;
+ for (auto &port: wr_ports) {
+ if (!port.clk_enable)
+ return false;
+ if (port.clk != clk)
+ return false;
+ if (port.clk_polarity != clk_polarity)
+ return false;
+ }
+ bool found_read_first = false;
+ for (auto &port: rd_ports) {
+ if (!port.clk_enable)
+ return false;
+ if (port.clk != clk)
+ return false;
+ if (port.clk_polarity != clk_polarity)
+ return false;
+ // No point doing this operation if there is no read-first relationship
+ // in the first place.
+ for (int j = 0; j < GetSize(wr_ports); j++)
+ if (!port.transparency_mask[j] && !port.collision_x_mask[j])
+ found_read_first = true;
+ }
+ return found_read_first;
+}
+
+void Mem::emulate_read_first(FfInitVals *initvals) {
+ log_assert(emulate_read_first_ok());
+ for (int i = 0; i < GetSize(rd_ports); i++)
+ for (int j = 0; j < GetSize(wr_ports); j++)
+ if (rd_ports[i].transparency_mask[j])
+ emulate_transparency(j, i, initvals);
+ for (int i = 0; i < GetSize(rd_ports); i++)
+ for (int j = 0; j < GetSize(wr_ports); j++) {
+ log_assert(!rd_ports[i].transparency_mask[j]);
+ rd_ports[i].collision_x_mask[j] = false;
+ rd_ports[i].transparency_mask[j] = true;
+ }
+ for (auto &port: wr_ports) {
+ Wire *new_data = module->addWire(NEW_ID, GetSize(port.data));
+ Wire *new_addr = module->addWire(NEW_ID, GetSize(port.addr));
+ auto compressed = port.compress_en();
+ Wire *new_en = module->addWire(NEW_ID, GetSize(compressed.first));
+ FfData ff_data(module, initvals, NEW_ID);
+ FfData ff_addr(module, initvals, NEW_ID);
+ FfData ff_en(module, initvals, NEW_ID);
+ ff_data.width = GetSize(port.data);
+ ff_data.has_clk = true;
+ ff_data.sig_clk = port.clk;
+ ff_data.pol_clk = port.clk_polarity;
+ ff_data.sig_d = port.data;
+ ff_data.sig_q = new_data;;
+ ff_data.val_init = Const(State::Sx, ff_data.width);
+ ff_data.emit();
+ ff_addr.width = GetSize(port.addr);
+ ff_addr.has_clk = true;
+ ff_addr.sig_clk = port.clk;
+ ff_addr.pol_clk = port.clk_polarity;
+ ff_addr.sig_d = port.addr;
+ ff_addr.sig_q = new_addr;;
+ ff_addr.val_init = Const(State::Sx, ff_addr.width);
+ ff_addr.emit();
+ ff_en.width = GetSize(compressed.first);
+ ff_en.has_clk = true;
+ ff_en.sig_clk = port.clk;
+ ff_en.pol_clk = port.clk_polarity;
+ ff_en.sig_d = compressed.first;
+ ff_en.sig_q = new_en;;
+ ff_en.val_init = Const(State::S0, ff_en.width);
+ ff_en.emit();
+ port.data = new_data;
+ port.addr = new_addr;
+ port.en = port.decompress_en(compressed.second, new_en);
+ }
+}
+
+std::pair<SigSpec, std::vector<int>> MemWr::compress_en() {
+ SigSpec sig = en[0];
+ std::vector<int> swizzle;
+ SigBit prev_bit = en[0];
+ int idx = 0;
+ for (auto &bit: en) {
+ if (bit != prev_bit) {
+ sig.append(bit);
+ prev_bit = bit;
+ idx++;
+ }
+ swizzle.push_back(idx);
+ }
+ log_assert(idx + 1 == GetSize(sig));
+ return {sig, swizzle};
+}
+
+SigSpec MemWr::decompress_en(const std::vector<int> &swizzle, SigSpec sig) {
+ SigSpec res;
+ for (int i: swizzle)
+ res.append(sig[i]);
+ return res;
+}
diff --git a/kernel/mem.h b/kernel/mem.h
index 87a148beb..ae87b1285 100644
--- a/kernel/mem.h
+++ b/kernel/mem.h
@@ -74,6 +74,9 @@ struct MemWr : RTLIL::AttrObject {
res[i] = State(sub >> i & 1);
return res;
}
+
+ std::pair<SigSpec, std::vector<int>> compress_en();
+ SigSpec decompress_en(const std::vector<int> &swizzle, SigSpec sig);
};
struct MemInit : RTLIL::AttrObject {
@@ -191,6 +194,33 @@ struct Mem : RTLIL::AttrObject {
// original address.
void widen_wr_port(int idx, int wide_log2);
+ // Emulates a sync read port's enable functionality in soft logic,
+ // changing the actual read port's enable to be always-on.
+ void emulate_rden(int idx, FfInitVals *initvals);
+
+ // Emulates a sync read port's initial/reset value functionality in
+ // soft logic, removing it from the actual read port.
+ void emulate_reset(int idx, bool emu_init, bool emu_arst, bool emu_srst, FfInitVals *initvals);
+
+ // Given a read port with ce_over_srst set, converts it to a port
+ // with ce_over_srst unset without changing its behavior by adding
+ // emulation logic.
+ void emulate_rd_ce_over_srst(int idx);
+
+ // Given a read port with ce_over_srst unset, converts it to a port
+ // with ce_over_srst set without changing its behavior by adding
+ // emulation logic.
+ void emulate_rd_srst_over_ce(int idx);
+
+ // Returns true iff emulate_read_first makes sense to call.
+ bool emulate_read_first_ok();
+
+ // Emulates all read-first read-write port relationships in terms of
+ // all-transparent ports, by delaying all write ports by one cycle.
+ // This can only be used when all read ports and all write ports are
+ // in the same clock domain.
+ void emulate_read_first(FfInitVals *initvals);
+
Mem(Module *module, IdString memid, int width, int start_offset, int size) : module(module), memid(memid), packed(false), mem(nullptr), cell(nullptr), width(width), start_offset(start_offset), size(size) {}
};
diff --git a/kernel/qcsat.cc b/kernel/qcsat.cc
index b7da958db..aaee984fb 100644
--- a/kernel/qcsat.cc
+++ b/kernel/qcsat.cc
@@ -84,7 +84,7 @@ int QuickConeSat::cell_complexity(RTLIL::Cell *cell)
ID($reduce_xnor), ID($reduce_bool),
ID($logic_not), ID($logic_and), ID($logic_or),
ID($eq), ID($ne), ID($eqx), ID($nex), ID($fa),
- ID($mux), ID($pmux), ID($lut), ID($sop),
+ ID($mux), ID($pmux), ID($bmux), ID($demux), ID($lut), ID($sop),
ID($_NOT_), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_),
ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_),
ID($_MUX_), ID($_NMUX_), ID($_MUX4_), ID($_MUX8_), ID($_MUX16_),
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index cd0f5ab12..72dcb89af 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -207,6 +207,7 @@ RTLIL::Const::Const()
RTLIL::Const::Const(std::string str)
{
flags = RTLIL::CONST_FLAG_STRING;
+ bits.reserve(str.size() * 8);
for (int i = str.size()-1; i >= 0; i--) {
unsigned char ch = str[i];
for (int j = 0; j < 8; j++) {
@@ -219,6 +220,7 @@ RTLIL::Const::Const(std::string str)
RTLIL::Const::Const(int val, int width)
{
flags = RTLIL::CONST_FLAG_NONE;
+ bits.reserve(width);
for (int i = 0; i < width; i++) {
bits.push_back((val & 1) != 0 ? State::S1 : State::S0);
val = val >> 1;
@@ -228,6 +230,7 @@ RTLIL::Const::Const(int val, int width)
RTLIL::Const::Const(RTLIL::State bit, int width)
{
flags = RTLIL::CONST_FLAG_NONE;
+ bits.reserve(width);
for (int i = 0; i < width; i++)
bits.push_back(bit);
}
@@ -235,6 +238,7 @@ RTLIL::Const::Const(RTLIL::State bit, int width)
RTLIL::Const::Const(const std::vector<bool> &bits)
{
flags = RTLIL::CONST_FLAG_NONE;
+ this->bits.reserve(bits.size());
for (const auto &b : bits)
this->bits.emplace_back(b ? State::S1 : State::S0);
}
@@ -242,6 +246,7 @@ RTLIL::Const::Const(const std::vector<bool> &bits)
RTLIL::Const::Const(const RTLIL::Const &c)
{
flags = c.flags;
+ this->bits.reserve(c.size());
for (const auto &b : c.bits)
this->bits.push_back(b);
}
@@ -1251,6 +1256,22 @@ namespace {
return;
}
+ if (cell->type == ID($bmux)) {
+ port(ID::A, param(ID::WIDTH) << param(ID::S_WIDTH));
+ port(ID::S, param(ID::S_WIDTH));
+ port(ID::Y, param(ID::WIDTH));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == ID($demux)) {
+ port(ID::A, param(ID::WIDTH));
+ port(ID::S, param(ID::S_WIDTH));
+ port(ID::Y, param(ID::WIDTH) << param(ID::S_WIDTH));
+ check_expected();
+ return;
+ }
+
if (cell->type == ID($lut)) {
param(ID::LUT);
port(ID::A, param(ID::WIDTH));
@@ -2444,6 +2465,26 @@ DEF_METHOD(Mux, ID($mux), 0)
DEF_METHOD(Pmux, ID($pmux), 1)
#undef DEF_METHOD
+#define DEF_METHOD(_func, _type, _demux) \
+ RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src) { \
+ RTLIL::Cell *cell = addCell(name, _type); \
+ cell->parameters[ID::WIDTH] = _demux ? sig_a.size() : sig_y.size(); \
+ cell->parameters[ID::S_WIDTH] = sig_s.size(); \
+ cell->setPort(ID::A, sig_a); \
+ cell->setPort(ID::S, sig_s); \
+ cell->setPort(ID::Y, sig_y); \
+ cell->set_src_attribute(src); \
+ return cell; \
+ } \
+ RTLIL::SigSpec RTLIL::Module::_func(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const std::string &src) { \
+ RTLIL::SigSpec sig_y = addWire(NEW_ID, _demux ? sig_a.size() << sig_s.size() : sig_a.size() >> sig_s.size()); \
+ add ## _func(name, sig_a, sig_s, sig_y, src); \
+ return sig_y; \
+ }
+DEF_METHOD(Bmux, ID($bmux), 0)
+DEF_METHOD(Demux, ID($demux), 1)
+#undef DEF_METHOD
+
#define DEF_METHOD_2(_func, _type, _P1, _P2) \
RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, const RTLIL::SigBit &sig1, const RTLIL::SigBit &sig2, const std::string &src) { \
RTLIL::Cell *cell = addCell(name, _type); \
@@ -3358,14 +3399,21 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed)
type.begins_with("$verific$") || type.begins_with("$array:") || type.begins_with("$extern:"))
return;
- if (type == ID($mux) || type == ID($pmux)) {
+ if (type == ID($mux) || type == ID($pmux) || type == ID($bmux)) {
parameters[ID::WIDTH] = GetSize(connections_[ID::Y]);
- if (type == ID($pmux))
+ if (type != ID($mux))
parameters[ID::S_WIDTH] = GetSize(connections_[ID::S]);
check();
return;
}
+ if (type == ID($demux)) {
+ parameters[ID::WIDTH] = GetSize(connections_[ID::A]);
+ parameters[ID::S_WIDTH] = GetSize(connections_[ID::S]);
+ check();
+ return;
+ }
+
if (type == ID($lut) || type == ID($sop)) {
parameters[ID::WIDTH] = GetSize(connections_[ID::A]);
return;
diff --git a/kernel/rtlil.h b/kernel/rtlil.h
index a562d253c..d8300f159 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -485,6 +485,9 @@ namespace RTLIL
RTLIL::Const const_pos (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
RTLIL::Const const_neg (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_bmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2);
+ RTLIL::Const const_demux (const RTLIL::Const &arg1, const RTLIL::Const &arg2);
+
// This iterator-range-pair is used for Design::modules(), Module::wires() and Module::cells().
// It maintains a reference counter that is used to make sure that the container is not modified while being iterated over.
@@ -1296,6 +1299,8 @@ public:
RTLIL::Cell* addMux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = "");
RTLIL::Cell* addPmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = "");
+ RTLIL::Cell* addBmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = "");
+ RTLIL::Cell* addDemux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = "");
RTLIL::Cell* addSlice (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, RTLIL::Const offset, const std::string &src = "");
RTLIL::Cell* addConcat (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, const std::string &src = "");
@@ -1421,6 +1426,8 @@ public:
RTLIL::SigSpec Mux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const std::string &src = "");
RTLIL::SigSpec Pmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const std::string &src = "");
+ RTLIL::SigSpec Bmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const std::string &src = "");
+ RTLIL::SigSpec Demux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const std::string &src = "");
RTLIL::SigBit BufGate (RTLIL::IdString name, const RTLIL::SigBit &sig_a, const std::string &src = "");
RTLIL::SigBit NotGate (RTLIL::IdString name, const RTLIL::SigBit &sig_a, const std::string &src = "");
diff --git a/kernel/satgen.cc b/kernel/satgen.cc
index 214826f5a..9c40ec66d 100644
--- a/kernel/satgen.cc
+++ b/kernel/satgen.cc
@@ -252,6 +252,106 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
return true;
}
+ if (cell->type == ID($bmux))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> s = importDefSigSpec(cell->getPort(ID::S), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+ std::vector<int> undef_a, undef_s, undef_y;
+
+ if (model_undef)
+ {
+ undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ undef_s = importUndefSigSpec(cell->getPort(ID::S), timestep);
+ undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ }
+
+ if (GetSize(s) == 0) {
+ ez->vec_set(a, y);
+ if (model_undef)
+ ez->vec_set(undef_a, undef_y);
+ } else {
+ for (int i = GetSize(s)-1; i >= 0; i--)
+ {
+ std::vector<int> out = (i == 0) ? y : ez->vec_var(a.size() / 2);
+ std::vector<int> yy = model_undef ? ez->vec_var(out.size()) : out;
+
+ std::vector<int> a0(a.begin(), a.begin() + a.size() / 2);
+ std::vector<int> a1(a.begin() + a.size() / 2, a.end());
+ ez->assume(ez->vec_eq(ez->vec_ite(s.at(i), a1, a0), yy));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_out = (i == 0) ? undef_y : ez->vec_var(a.size() / 2);
+ std::vector<int> undef_a0(undef_a.begin(), undef_a.begin() + a.size() / 2);
+ std::vector<int> undef_a1(undef_a.begin() + a.size() / 2, undef_a.end());
+ std::vector<int> unequal_ab = ez->vec_not(ez->vec_iff(a0, a1));
+ std::vector<int> undef_ab = ez->vec_or(unequal_ab, ez->vec_or(undef_a0, undef_a1));
+ std::vector<int> yX = ez->vec_ite(undef_s.at(i), undef_ab, ez->vec_ite(s.at(i), undef_a1, undef_a0));
+ ez->assume(ez->vec_eq(yX, undef_out));
+ undefGating(out, yy, undef_out);
+
+ undef_a = undef_out;
+ }
+
+ a = out;
+ }
+ }
+ return true;
+ }
+
+ if (cell->type == ID($demux))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> s = importDefSigSpec(cell->getPort(ID::S), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+ std::vector<int> undef_a, undef_s, undef_y;
+
+ if (model_undef)
+ {
+ undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ undef_s = importUndefSigSpec(cell->getPort(ID::S), timestep);
+ undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ }
+
+ if (GetSize(s) == 0) {
+ ez->vec_set(a, y);
+ if (model_undef)
+ ez->vec_set(undef_a, undef_y);
+ } else {
+ for (int i = 0; i < (1 << GetSize(s)); i++)
+ {
+ std::vector<int> ss;
+ for (int j = 0; j < GetSize(s); j++) {
+ if (i & 1 << j)
+ ss.push_back(s[j]);
+ else
+ ss.push_back(ez->NOT(s[j]));
+ }
+ int sss = ez->expression(ezSAT::OpAnd, ss);
+
+ for (int j = 0; j < GetSize(a); j++) {
+ ez->SET(ez->AND(sss, a[j]), yy.at(i * GetSize(a) + j));
+ }
+
+ if (model_undef)
+ {
+ int s0 = ez->expression(ezSAT::OpOr, ez->vec_and(ez->vec_not(ss), ez->vec_not(undef_s)));
+ int us = ez->AND(ez->NOT(s0), ez->expression(ezSAT::OpOr, undef_s));
+ for (int j = 0; j < GetSize(a); j++) {
+ int a0 = ez->AND(ez->NOT(a[j]), ez->NOT(undef_a[j]));
+ int yX = ez->AND(ez->OR(us, undef_a[j]), ez->NOT(ez->OR(s0, a0)));
+ ez->SET(yX, undef_y.at(i * GetSize(a) + j));
+ }
+ }
+ }
+ if (model_undef)
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
if (cell->type == ID($pmux))
{
std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
diff --git a/kernel/yosys.cc b/kernel/yosys.cc
index 102f9e737..09909696b 100644
--- a/kernel/yosys.cc
+++ b/kernel/yosys.cc
@@ -618,6 +618,23 @@ RTLIL::IdString new_id(std::string file, int line, std::string func)
return stringf("$auto$%s:%d:%s$%d", file.c_str(), line, func.c_str(), autoidx++);
}
+RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std::string suffix)
+{
+#ifdef _WIN32
+ size_t pos = file.find_last_of("/\\");
+#else
+ size_t pos = file.find_last_of('/');
+#endif
+ if (pos != std::string::npos)
+ file = file.substr(pos+1);
+
+ pos = func.find_last_of(':');
+ if (pos != std::string::npos)
+ func = func.substr(pos+1);
+
+ return stringf("$auto$%s:%d:%s$%s$%d", file.c_str(), line, func.c_str(), suffix.c_str(), autoidx++);
+}
+
RTLIL::Design *yosys_get_design()
{
return yosys_design;
diff --git a/kernel/yosys.h b/kernel/yosys.h
index 091e2282f..93e7ff23e 100644
--- a/kernel/yosys.h
+++ b/kernel/yosys.h
@@ -322,9 +322,12 @@ Tcl_Interp *yosys_get_tcl_interp();
extern RTLIL::Design *yosys_design;
RTLIL::IdString new_id(std::string file, int line, std::string func);
+RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std::string suffix);
#define NEW_ID \
YOSYS_NAMESPACE_PREFIX new_id(__FILE__, __LINE__, __FUNCTION__)
+#define NEW_ID_SUFFIX(suffix) \
+ YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix)
// Create a statically allocated IdString object, using for example ID::A or ID($add).
//