aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/binding.cc29
-rw-r--r--kernel/binding.h60
-rw-r--r--kernel/bitpattern.h2
-rw-r--r--kernel/calc.cc53
-rw-r--r--kernel/cellaigs.cc2
-rw-r--r--kernel/cellaigs.h2
-rw-r--r--kernel/celledges.cc42
-rw-r--r--kernel/celledges.h2
-rw-r--r--kernel/celltypes.h46
-rw-r--r--kernel/consteval.h37
-rw-r--r--kernel/constids.inc22
-rw-r--r--kernel/cost.h2
-rw-r--r--kernel/driver.cc42
-rw-r--r--kernel/ff.cc755
-rw-r--r--kernel/ff.h534
-rw-r--r--kernel/ffinit.h1
-rw-r--r--kernel/ffmerge.cc359
-rw-r--r--kernel/ffmerge.h141
-rw-r--r--kernel/fstdata.cc252
-rw-r--r--kernel/fstdata.h81
-rw-r--r--kernel/hashlib.h8
-rw-r--r--kernel/log.cc24
-rw-r--r--kernel/log.h28
-rw-r--r--kernel/macc.h2
-rw-r--r--kernel/mem.cc1386
-rw-r--r--kernel/mem.h185
-rw-r--r--kernel/modtools.h10
-rw-r--r--kernel/qcsat.cc102
-rw-r--r--kernel/qcsat.h76
-rw-r--r--kernel/register.cc2
-rw-r--r--kernel/register.h2
-rw-r--r--kernel/rtlil.cc481
-rw-r--r--kernel/rtlil.h99
-rw-r--r--kernel/satgen.cc122
-rw-r--r--kernel/satgen.h2
-rw-r--r--kernel/sigtools.h2
-rw-r--r--kernel/timinginfo.h50
-rw-r--r--kernel/utils.h2
-rw-r--r--kernel/yosys.cc43
-rw-r--r--kernel/yosys.h14
40 files changed, 4431 insertions, 673 deletions
diff --git a/kernel/binding.cc b/kernel/binding.cc
new file mode 100644
index 000000000..621f7007b
--- /dev/null
+++ b/kernel/binding.cc
@@ -0,0 +1,29 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "binding.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+RTLIL::Binding::Binding(RTLIL::IdString target_type,
+ RTLIL::IdString target_name)
+ : target_type(target_type), target_name(target_name)
+{}
+
+YOSYS_NAMESPACE_END
diff --git a/kernel/binding.h b/kernel/binding.h
new file mode 100644
index 000000000..3b64e76da
--- /dev/null
+++ b/kernel/binding.h
@@ -0,0 +1,60 @@
+/* -*- c++ -*-
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef BINDING_H
+#define BINDING_H
+
+#include "kernel/rtlil.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct RTLIL::Binding
+{
+ // Represents a bind construct.
+ //
+ // The target of the binding is represented by target_type and
+ // target_name (see comments above the fields).
+
+ Binding(RTLIL::IdString target_type,
+ RTLIL::IdString target_name);
+
+ virtual ~Binding() {}
+
+ // Return a string describing the binding
+ virtual std::string describe() const = 0;
+
+protected:
+ // May be empty. If not, it's the name of the module or interface to
+ // bind to.
+ RTLIL::IdString target_type;
+
+ // If target_type is nonempty (the usual case), this is a hierarchical
+ // reference to the bind target. If target_type is empty, we have to
+ // wait until the hierarchy pass to figure out whether this was the name
+ // of a module/interface type or an instance.
+ RTLIL::IdString target_name;
+
+ // An attribute name which contains an ID that's unique across binding
+ // instances (used to ensure we don't apply a binding twice to a module)
+ RTLIL::IdString attr_name;
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h
index 894a95ed1..7a8eb39f9 100644
--- a/kernel/bitpattern.h
+++ b/kernel/bitpattern.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/kernel/calc.cc b/kernel/calc.cc
index d54ccbc10..0865db526 100644
--- a/kernel/calc.cc
+++ b/kernel/calc.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -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/cellaigs.cc b/kernel/cellaigs.cc
index 2c82b1bca..292af3f51 100644
--- a/kernel/cellaigs.cc
+++ b/kernel/cellaigs.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/kernel/cellaigs.h b/kernel/cellaigs.h
index 1417a614c..8f6d69ba6 100644
--- a/kernel/cellaigs.h
+++ b/kernel/cellaigs.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/kernel/celledges.cc b/kernel/celledges.cc
index 314e7c77e..c43ba8db3 100644
--- a/kernel/celledges.cc
+++ b/kernel/celledges.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -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/celledges.h b/kernel/celledges.h
index d105e4009..d5e374f05 100644
--- a/kernel/celledges.h
+++ b/kernel/celledges.h
@@ -1,7 +1,7 @@
/* -*- c++ -*-
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/kernel/celltypes.h b/kernel/celltypes.h
index 944cb301a..7e9cfb38d 100644
--- a/kernel/celltypes.h
+++ b/kernel/celltypes.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -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);
@@ -142,6 +145,8 @@ struct CellTypes
setup_type(ID($dffsre), {ID::CLK, ID::SET, ID::CLR, ID::D, ID::EN}, {ID::Q});
setup_type(ID($adff), {ID::CLK, ID::ARST, ID::D}, {ID::Q});
setup_type(ID($adffe), {ID::CLK, ID::ARST, ID::D, ID::EN}, {ID::Q});
+ setup_type(ID($aldff), {ID::CLK, ID::ALOAD, ID::AD, ID::D}, {ID::Q});
+ setup_type(ID($aldffe), {ID::CLK, ID::ALOAD, ID::AD, ID::D, ID::EN}, {ID::Q});
setup_type(ID($sdff), {ID::CLK, ID::SRST, ID::D}, {ID::Q});
setup_type(ID($sdffe), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q});
setup_type(ID($sdffce), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q});
@@ -155,9 +160,13 @@ struct CellTypes
setup_internals_ff();
setup_type(ID($memrd), {ID::CLK, ID::EN, ID::ADDR}, {ID::DATA});
+ setup_type(ID($memrd_v2), {ID::CLK, ID::EN, ID::ARST, ID::SRST, ID::ADDR}, {ID::DATA});
setup_type(ID($memwr), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, pool<RTLIL::IdString>());
+ setup_type(ID($memwr_v2), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, pool<RTLIL::IdString>());
setup_type(ID($meminit), {ID::ADDR, ID::DATA}, pool<RTLIL::IdString>());
+ setup_type(ID($meminit_v2), {ID::ADDR, ID::DATA, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($mem), {ID::RD_CLK, ID::RD_EN, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA});
+ setup_type(ID($mem_v2), {ID::RD_CLK, ID::RD_EN, ID::RD_ARST, ID::RD_SRST, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA});
setup_type(ID($fsm), {ID::CLK, ID::ARST, ID::CTRL_IN}, {ID::CTRL_OUT});
}
@@ -222,6 +231,15 @@ struct CellTypes
for (auto c1 : list_np)
for (auto c2 : list_np)
+ setup_type(stringf("$_ALDFF_%c%c_", c1, c2), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q});
+
+ for (auto c1 : list_np)
+ for (auto c2 : list_np)
+ for (auto c3 : list_np)
+ setup_type(stringf("$_ALDFFE_%c%c%c_", c1, c2, c3), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q});
+
+ for (auto c1 : list_np)
+ for (auto c2 : list_np)
for (auto c3 : list_np)
setup_type(stringf("$_DFFSR_%c%c%c_", c1, c2, c3), {ID::C, ID::S, ID::R, ID::D}, {ID::Q});
@@ -396,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();
@@ -405,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 ff8cf86d6..642eb42b2 100644
--- a/kernel/consteval.h
+++ b/kernel/consteval.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -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/constids.inc b/kernel/constids.inc
index 3c2ff9beb..566b76217 100644
--- a/kernel/constids.inc
+++ b/kernel/constids.inc
@@ -11,9 +11,12 @@ X(abc9_mergeability)
X(abc9_scc_id)
X(abcgroup)
X(ABITS)
+X(AD)
X(ADDR)
X(allconst)
X(allseq)
+X(ALOAD)
+X(ALOAD_POLARITY)
X(always_comb)
X(always_ff)
X(always_latch)
@@ -32,6 +35,7 @@ X(bugpoint_keep)
X(B_WIDTH)
X(C)
X(cells_not_processed)
+X(CE_OVER_SRST)
X(CFG_ABITS)
X(CFG_DBITS)
X(CFG_INIT)
@@ -46,6 +50,7 @@ X(CLK_POLARITY)
X(CLR)
X(CLR_POLARITY)
X(CO)
+X(COLLISION_X_MASK)
X(CONFIG)
X(CONFIG_WIDTH)
X(CTRL_IN)
@@ -95,6 +100,7 @@ X(hdlname)
X(hierconn)
X(I)
X(INIT)
+X(INIT_VALUE)
X(init)
X(initial_top)
X(interface_modport)
@@ -133,19 +139,31 @@ X(onehot)
X(P)
X(parallel_case)
X(parameter)
+X(PORTID)
X(PRIORITY)
+X(PRIORITY_MASK)
X(Q)
X(qwp_position)
X(R)
X(RD_ADDR)
+X(RD_ARST)
+X(RD_ARST_VALUE)
+X(RD_CE_OVER_SRST)
X(RD_CLK)
X(RD_CLK_ENABLE)
X(RD_CLK_POLARITY)
+X(RD_COLLISION_X_MASK)
X(RD_DATA)
X(RD_EN)
+X(RD_INIT_VALUE)
X(RD_PORTS)
+X(RD_SRST)
+X(RD_SRST_VALUE)
+X(RD_TRANSPARENCY_MASK)
X(RD_TRANSPARENT)
+X(RD_WIDE_CONTINUATION)
X(reg)
+X(reprocess_after)
X(S)
X(SET)
X(SET_POLARITY)
@@ -161,6 +179,7 @@ X(SRC_WIDTH)
X(SRST)
X(SRST_POLARITY)
X(SRST_VALUE)
+X(sta_arrival)
X(STATE_BITS)
X(STATE_NUM)
X(STATE_NUM_LOG2)
@@ -195,6 +214,7 @@ X(T_LIMIT_TYP)
X(to_delete)
X(top)
X(TRANS_NUM)
+X(TRANSPARENCY_MASK)
X(TRANSPARENT)
X(TRANS_TABLE)
X(T_RISE_MAX)
@@ -220,6 +240,8 @@ X(WR_CLK_POLARITY)
X(WR_DATA)
X(WR_EN)
X(WR_PORTS)
+X(WR_PRIORITY_MASK)
+X(WR_WIDE_CONTINUATION)
X(X)
X(Y)
X(Y_WIDTH)
diff --git a/kernel/cost.h b/kernel/cost.h
index ea2a4c1f0..b81420af7 100644
--- a/kernel/cost.h
+++ b/kernel/cost.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/kernel/driver.cc b/kernel/driver.cc
index b55f02837..f8f940e89 100644
--- a/kernel/driver.cc
+++ b/kernel/driver.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -118,7 +118,7 @@ int main(int argc, char **argv)
if (argc == 2)
{
// Run the first argument as a script file
- run_frontend(argv[1], "script", 0, 0, 0);
+ run_frontend(argv[1], "script");
}
}
@@ -202,12 +202,13 @@ int main(int argc, char **argv)
std::string output_filename = "";
std::string scriptfile = "";
std::string depsfile = "";
+ std::string topmodule = "";
bool scriptfile_tcl = false;
- bool got_output_filename = false;
bool print_banner = true;
bool print_stats = true;
bool call_abort = false;
bool timing_details = false;
+ bool run_shell = true;
bool mode_v = false;
bool mode_q = false;
@@ -288,6 +289,9 @@ int main(int argc, char **argv)
printf(" -A\n");
printf(" will call abort() at the end of the script. for debugging\n");
printf("\n");
+ printf(" -r <module_name>\n");
+ printf(" elaborate command line arguments using the specified top module\n");
+ printf("\n");
printf(" -D <macro>[=<value>]\n");
printf(" set the specified Verilog define (via \"read -define\")\n");
printf("\n");
@@ -342,7 +346,7 @@ int main(int argc, char **argv)
}
int opt;
- while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:x:")) != -1)
+ while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:r:D:P:E:x:")) != -1)
{
switch (opt)
{
@@ -366,6 +370,7 @@ int main(int argc, char **argv)
exit(0);
case 'S':
passes_commands.push_back("synth");
+ run_shell = false;
break;
case 'g':
log_force_debug++;
@@ -378,19 +383,23 @@ int main(int argc, char **argv)
break;
case 'H':
passes_commands.push_back("help");
+ run_shell = false;
break;
case 'h':
passes_commands.push_back(stringf("help %s", optarg));
+ run_shell = false;
break;
case 'b':
backend_command = optarg;
+ run_shell = false;
break;
case 'p':
passes_commands.push_back(optarg);
+ run_shell = false;
break;
case 'o':
output_filename = optarg;
- got_output_filename = true;
+ run_shell = false;
break;
case 'l':
case 'L':
@@ -422,10 +431,12 @@ int main(int argc, char **argv)
case 's':
scriptfile = optarg;
scriptfile_tcl = false;
+ run_shell = false;
break;
case 'c':
scriptfile = optarg;
scriptfile_tcl = true;
+ run_shell = false;
break;
case 'W':
log_warn_regexes.push_back(YS_REGEX_COMPILE(optarg));
@@ -436,6 +447,9 @@ int main(int argc, char **argv)
case 'e':
log_werror_regexes.push_back(YS_REGEX_COMPILE(optarg));
break;
+ case 'r':
+ topmodule = optarg;
+ break;
case 'D':
vlog_defines.push_back(optarg);
break;
@@ -506,12 +520,6 @@ int main(int argc, char **argv)
for (auto &fn : plugin_filenames)
load_plugin(fn, {});
- if (optind == argc && passes_commands.size() == 0 && scriptfile.empty()) {
- if (!got_output_filename)
- backend_command = "";
- shell(yosys_design);
- }
-
if (!vlog_defines.empty()) {
std::string vdef_cmd = "read -define";
for (auto vdef : vlog_defines)
@@ -520,7 +528,11 @@ int main(int argc, char **argv)
}
while (optind < argc)
- run_frontend(argv[optind++], frontend_command, output_filename == "-" ? &backend_command : NULL);
+ if (run_frontend(argv[optind++], frontend_command))
+ run_shell = false;
+
+ if (!topmodule.empty())
+ run_pass("hierarchy -top " + topmodule);
if (!scriptfile.empty()) {
if (scriptfile_tcl) {
@@ -531,13 +543,15 @@ int main(int argc, char **argv)
log_error("Can't exectue TCL script: this version of yosys is not built with TCL support enabled.\n");
#endif
} else
- run_frontend(scriptfile, "script", output_filename == "-" ? &backend_command : NULL);
+ run_frontend(scriptfile, "script");
}
for (auto it = passes_commands.begin(); it != passes_commands.end(); it++)
run_pass(*it);
- if (!backend_command.empty())
+ if (run_shell)
+ shell(yosys_design);
+ else
run_backend(output_filename, backend_command);
yosys_design->check();
diff --git a/kernel/ff.cc b/kernel/ff.cc
new file mode 100644
index 000000000..c43482bd2
--- /dev/null
+++ b/kernel/ff.cc
@@ -0,0 +1,755 @@
+/*
+ * 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/ff.h"
+
+USING_YOSYS_NAMESPACE
+
+FfData::FfData(FfInitVals *initvals, Cell *cell_) : FfData(cell_->module, initvals, cell_->name)
+{
+ cell = cell_;
+ sig_q = cell->getPort(ID::Q);
+ width = GetSize(sig_q);
+ attributes = cell->attributes;
+
+ if (initvals)
+ val_init = (*initvals)(sig_q);
+
+ std::string type_str = cell->type.str();
+
+ if (cell->type.in(ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) {
+ if (cell->type == ID($ff)) {
+ has_gclk = true;
+ sig_d = cell->getPort(ID::D);
+ } else if (cell->type == ID($sr)) {
+ // No data input at all.
+ } else if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) {
+ has_aload = true;
+ sig_aload = cell->getPort(ID::EN);
+ pol_aload = cell->getParam(ID::EN_POLARITY).as_bool();
+ sig_ad = cell->getPort(ID::D);
+ } else {
+ has_clk = true;
+ sig_clk = cell->getPort(ID::CLK);
+ pol_clk = cell->getParam(ID::CLK_POLARITY).as_bool();
+ sig_d = cell->getPort(ID::D);
+ }
+ if (cell->type.in(ID($dffe), ID($dffsre), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce))) {
+ has_ce = true;
+ sig_ce = cell->getPort(ID::EN);
+ pol_ce = cell->getParam(ID::EN_POLARITY).as_bool();
+ }
+ if (cell->type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) {
+ has_sr = true;
+ sig_clr = cell->getPort(ID::CLR);
+ sig_set = cell->getPort(ID::SET);
+ pol_clr = cell->getParam(ID::CLR_POLARITY).as_bool();
+ pol_set = cell->getParam(ID::SET_POLARITY).as_bool();
+ }
+ if (cell->type.in(ID($aldff), ID($aldffe))) {
+ has_aload = true;
+ sig_aload = cell->getPort(ID::ALOAD);
+ pol_aload = cell->getParam(ID::ALOAD_POLARITY).as_bool();
+ sig_ad = cell->getPort(ID::AD);
+ }
+ if (cell->type.in(ID($adff), ID($adffe), ID($adlatch))) {
+ has_arst = true;
+ sig_arst = cell->getPort(ID::ARST);
+ pol_arst = cell->getParam(ID::ARST_POLARITY).as_bool();
+ val_arst = cell->getParam(ID::ARST_VALUE);
+ }
+ if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) {
+ has_srst = true;
+ sig_srst = cell->getPort(ID::SRST);
+ pol_srst = cell->getParam(ID::SRST_POLARITY).as_bool();
+ val_srst = cell->getParam(ID::SRST_VALUE);
+ ce_over_srst = cell->type == ID($sdffce);
+ }
+ } else if (cell->type == ID($_FF_)) {
+ is_fine = true;
+ has_gclk = true;
+ sig_d = cell->getPort(ID::D);
+ } else if (type_str.substr(0, 5) == "$_SR_") {
+ is_fine = true;
+ has_sr = true;
+ pol_set = type_str[5] == 'P';
+ pol_clr = type_str[6] == 'P';
+ sig_set = cell->getPort(ID::S);
+ sig_clr = cell->getPort(ID::R);
+ } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 8) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[6] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 10) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[7] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_ce = true;
+ pol_ce = type_str[8] == 'P';
+ sig_ce = cell->getPort(ID::E);
+ } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[6] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_arst = true;
+ pol_arst = type_str[7] == 'P';
+ sig_arst = cell->getPort(ID::R);
+ val_arst = type_str[8] == '1' ? State::S1 : State::S0;
+ } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 12) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[7] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_arst = true;
+ pol_arst = type_str[8] == 'P';
+ sig_arst = cell->getPort(ID::R);
+ val_arst = type_str[9] == '1' ? State::S1 : State::S0;
+ has_ce = true;
+ pol_ce = type_str[10] == 'P';
+ sig_ce = cell->getPort(ID::E);
+ } else if (type_str.substr(0, 8) == "$_ALDFF_" && type_str.size() == 11) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[8] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_aload = true;
+ pol_aload = type_str[9] == 'P';
+ sig_aload = cell->getPort(ID::L);
+ sig_ad = cell->getPort(ID::AD);
+ } else if (type_str.substr(0, 9) == "$_ALDFFE_" && type_str.size() == 13) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[9] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_aload = true;
+ pol_aload = type_str[10] == 'P';
+ sig_aload = cell->getPort(ID::L);
+ sig_ad = cell->getPort(ID::AD);
+ has_ce = true;
+ pol_ce = type_str[11] == 'P';
+ sig_ce = cell->getPort(ID::E);
+ } else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[8] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_sr = true;
+ pol_set = type_str[9] == 'P';
+ pol_clr = type_str[10] == 'P';
+ sig_set = cell->getPort(ID::S);
+ sig_clr = cell->getPort(ID::R);
+ } else if (type_str.substr(0, 9) == "$_DFFSRE_" && type_str.size() == 14) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[9] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_sr = true;
+ pol_set = type_str[10] == 'P';
+ pol_clr = type_str[11] == 'P';
+ sig_set = cell->getPort(ID::S);
+ sig_clr = cell->getPort(ID::R);
+ has_ce = true;
+ pol_ce = type_str[12] == 'P';
+ sig_ce = cell->getPort(ID::E);
+ } else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[7] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_srst = true;
+ pol_srst = type_str[8] == 'P';
+ sig_srst = cell->getPort(ID::R);
+ val_srst = type_str[9] == '1' ? State::S1 : State::S0;
+ } else if (type_str.substr(0, 8) == "$_SDFFE_" && type_str.size() == 13) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[8] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_srst = true;
+ pol_srst = type_str[9] == 'P';
+ sig_srst = cell->getPort(ID::R);
+ val_srst = type_str[10] == '1' ? State::S1 : State::S0;
+ has_ce = true;
+ pol_ce = type_str[11] == 'P';
+ sig_ce = cell->getPort(ID::E);
+ } else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[9] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_srst = true;
+ pol_srst = type_str[10] == 'P';
+ sig_srst = cell->getPort(ID::R);
+ val_srst = type_str[11] == '1' ? State::S1 : State::S0;
+ has_ce = true;
+ pol_ce = type_str[12] == 'P';
+ sig_ce = cell->getPort(ID::E);
+ ce_over_srst = true;
+ } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) {
+ is_fine = true;
+ has_aload = true;
+ sig_ad = cell->getPort(ID::D);
+ has_aload = true;
+ pol_aload = type_str[9] == 'P';
+ sig_aload = cell->getPort(ID::E);
+ } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) {
+ is_fine = true;
+ has_aload = true;
+ sig_ad = cell->getPort(ID::D);
+ has_aload = true;
+ pol_aload = type_str[9] == 'P';
+ sig_aload = cell->getPort(ID::E);
+ has_arst = true;
+ pol_arst = type_str[10] == 'P';
+ sig_arst = cell->getPort(ID::R);
+ val_arst = type_str[11] == '1' ? State::S1 : State::S0;
+ } else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) {
+ is_fine = true;
+ has_aload = true;
+ sig_ad = cell->getPort(ID::D);
+ has_aload = true;
+ pol_aload = type_str[11] == 'P';
+ sig_aload = cell->getPort(ID::E);
+ has_sr = true;
+ pol_set = type_str[12] == 'P';
+ pol_clr = type_str[13] == 'P';
+ sig_set = cell->getPort(ID::S);
+ sig_clr = cell->getPort(ID::R);
+ } else {
+ log_assert(0);
+ }
+ if (has_aload && !has_clk && !has_sr && !has_arst && sig_ad.is_fully_const()) {
+ // Plain D latches with const D treated specially.
+ has_aload = false;
+ has_arst = true;
+ sig_arst = sig_aload;
+ pol_arst = pol_aload;
+ val_arst = sig_ad.as_const();
+ }
+}
+
+FfData FfData::slice(const std::vector<int> &bits) {
+ FfData res(module, initvals, NEW_ID);
+ res.sig_clk = sig_clk;
+ res.sig_ce = sig_ce;
+ res.sig_aload = sig_aload;
+ res.sig_arst = sig_arst;
+ res.sig_srst = sig_srst;
+ res.has_clk = has_clk;
+ res.has_gclk = has_gclk;
+ res.has_ce = has_ce;
+ res.has_aload = has_aload;
+ res.has_arst = has_arst;
+ res.has_srst = has_srst;
+ res.has_sr = has_sr;
+ res.ce_over_srst = ce_over_srst;
+ res.is_fine = is_fine;
+ res.pol_clk = pol_clk;
+ res.pol_ce = pol_ce;
+ res.pol_aload = pol_aload;
+ res.pol_arst = pol_arst;
+ res.pol_srst = pol_srst;
+ res.pol_clr = pol_clr;
+ res.pol_set = pol_set;
+ res.attributes = attributes;
+ for (int i : bits) {
+ res.sig_q.append(sig_q[i]);
+ if (has_clk || has_gclk)
+ res.sig_d.append(sig_d[i]);
+ if (has_aload)
+ res.sig_ad.append(sig_ad[i]);
+ if (has_sr) {
+ res.sig_clr.append(sig_clr[i]);
+ res.sig_set.append(sig_set[i]);
+ }
+ if (has_arst)
+ res.val_arst.bits.push_back(val_arst[i]);
+ if (has_srst)
+ res.val_srst.bits.push_back(val_srst[i]);
+ if (initvals)
+ res.val_init.bits.push_back(val_init[i]);
+ }
+ res.width = GetSize(res.sig_q);
+ return res;
+}
+
+void FfData::add_dummy_ce() {
+ if (has_ce)
+ return;
+ has_ce = true;
+ pol_ce = true;
+ sig_ce = State::S1;
+ ce_over_srst = false;
+}
+
+void FfData::add_dummy_srst() {
+ if (has_srst)
+ return;
+ has_srst = true;
+ pol_srst = true;
+ sig_srst = State::S0;
+ val_srst = Const(State::Sx, width);
+ ce_over_srst = false;
+}
+
+void FfData::add_dummy_arst() {
+ if (has_arst)
+ return;
+ has_arst = true;
+ pol_arst = true;
+ sig_arst = State::S0;
+ val_arst = Const(State::Sx, width);
+}
+
+void FfData::add_dummy_aload() {
+ if (has_aload)
+ return;
+ has_aload = true;
+ pol_aload = true;
+ sig_aload = State::S0;
+ sig_ad = Const(State::Sx, width);
+}
+
+void FfData::add_dummy_sr() {
+ if (has_sr)
+ return;
+ has_sr = true;
+ pol_clr = true;
+ pol_set = true;
+ sig_clr = Const(State::S0, width);
+ sig_set = Const(State::S0, width);
+}
+
+void FfData::add_dummy_clk() {
+ if (has_clk)
+ return;
+ has_clk = true;
+ pol_clk = true;
+ sig_clk = State::S0;
+ sig_d = Const(State::Sx, width);
+}
+
+void FfData::arst_to_aload() {
+ log_assert(has_arst);
+ log_assert(!has_aload);
+ pol_aload = pol_arst;
+ sig_aload = sig_arst;
+ sig_ad = val_arst;
+ has_aload = true;
+ has_arst = false;
+}
+
+void FfData::arst_to_sr() {
+ log_assert(has_arst);
+ log_assert(!has_sr);
+ pol_clr = pol_arst;
+ pol_set = pol_arst;
+ sig_clr = Const(pol_arst ? State::S0 : State::S1, width);
+ sig_set = Const(pol_arst ? State::S0 : State::S1, width);
+ has_sr = true;
+ has_arst = false;
+ for (int i = 0; i < width; i++) {
+ if (val_arst[i] == State::S1)
+ sig_set[i] = sig_arst;
+ else
+ sig_clr[i] = sig_arst;
+ }
+}
+
+void FfData::aload_to_sr() {
+ log_assert(has_aload);
+ log_assert(!has_sr);
+ has_sr = true;
+ has_aload = false;
+ if (!is_fine) {
+ pol_clr = false;
+ pol_set = true;
+ if (pol_aload) {
+ sig_clr = module->Mux(NEW_ID, Const(State::S1, width), sig_ad, sig_aload);
+ sig_set = module->Mux(NEW_ID, Const(State::S0, width), sig_ad, sig_aload);
+ } else {
+ sig_clr = module->Mux(NEW_ID, sig_ad, Const(State::S1, width), sig_aload);
+ sig_set = module->Mux(NEW_ID, sig_ad, Const(State::S0, width), sig_aload);
+ }
+ } else {
+ pol_clr = pol_aload;
+ pol_set = pol_aload;
+ if (pol_aload) {
+ sig_clr = module->AndnotGate(NEW_ID, sig_aload, sig_ad);
+ sig_set = module->AndGate(NEW_ID, sig_aload, sig_ad);
+ } else {
+ sig_clr = module->OrGate(NEW_ID, sig_aload, sig_ad);
+ sig_set = module->OrnotGate(NEW_ID, sig_aload, sig_ad);
+ }
+ }
+}
+
+void FfData::convert_ce_over_srst(bool val) {
+ if (!has_ce || !has_srst || ce_over_srst == val)
+ return;
+ if (val) {
+ // sdffe to sdffce
+ if (!is_fine) {
+ if (pol_ce) {
+ if (pol_srst) {
+ sig_ce = module->Or(NEW_ID, sig_ce, sig_srst);
+ } else {
+ SigSpec tmp = module->Not(NEW_ID, sig_srst);
+ sig_ce = module->Or(NEW_ID, sig_ce, tmp);
+ }
+ } else {
+ if (pol_srst) {
+ SigSpec tmp = module->Not(NEW_ID, sig_srst);
+ sig_ce = module->And(NEW_ID, sig_ce, tmp);
+ } else {
+ sig_ce = module->And(NEW_ID, sig_ce, sig_srst);
+ }
+ }
+ } else {
+ if (pol_ce) {
+ if (pol_srst) {
+ sig_ce = module->OrGate(NEW_ID, sig_ce, sig_srst);
+ } else {
+ sig_ce = module->OrnotGate(NEW_ID, sig_ce, sig_srst);
+ }
+ } else {
+ if (pol_srst) {
+ sig_ce = module->AndnotGate(NEW_ID, sig_ce, sig_srst);
+ } else {
+ sig_ce = module->AndGate(NEW_ID, sig_ce, sig_srst);
+ }
+ }
+ }
+ } else {
+ // sdffce to sdffe
+ if (!is_fine) {
+ if (pol_srst) {
+ if (pol_ce) {
+ sig_srst = cell->module->And(NEW_ID, sig_srst, sig_ce);
+ } else {
+ SigSpec tmp = module->Not(NEW_ID, sig_ce);
+ sig_srst = cell->module->And(NEW_ID, sig_srst, tmp);
+ }
+ } else {
+ if (pol_ce) {
+ SigSpec tmp = module->Not(NEW_ID, sig_ce);
+ sig_srst = cell->module->Or(NEW_ID, sig_srst, tmp);
+ } else {
+ sig_srst = cell->module->Or(NEW_ID, sig_srst, sig_ce);
+ }
+ }
+ } else {
+ if (pol_srst) {
+ if (pol_ce) {
+ sig_srst = cell->module->AndGate(NEW_ID, sig_srst, sig_ce);
+ } else {
+ sig_srst = cell->module->AndnotGate(NEW_ID, sig_srst, sig_ce);
+ }
+ } else {
+ if (pol_ce) {
+ sig_srst = cell->module->OrnotGate(NEW_ID, sig_srst, sig_ce);
+ } else {
+ sig_srst = cell->module->OrGate(NEW_ID, sig_srst, sig_ce);
+ }
+ }
+ }
+ }
+ ce_over_srst = val;
+}
+
+void FfData::unmap_ce() {
+ if (!has_ce)
+ return;
+ log_assert(has_clk);
+ if (has_srst && ce_over_srst)
+ unmap_srst();
+
+ if (!is_fine) {
+ if (pol_ce)
+ sig_d = module->Mux(NEW_ID, sig_q, sig_d, sig_ce);
+ else
+ sig_d = module->Mux(NEW_ID, sig_d, sig_q, sig_ce);
+ } else {
+ if (pol_ce)
+ sig_d = module->MuxGate(NEW_ID, sig_q, sig_d, sig_ce);
+ else
+ sig_d = module->MuxGate(NEW_ID, sig_d, sig_q, sig_ce);
+ }
+ has_ce = false;
+}
+
+void FfData::unmap_srst() {
+ if (!has_srst)
+ return;
+ if (has_ce && !ce_over_srst)
+ unmap_ce();
+
+ if (!is_fine) {
+ if (pol_srst)
+ sig_d = module->Mux(NEW_ID, sig_d, val_srst, sig_srst);
+ else
+ sig_d = module->Mux(NEW_ID, val_srst, sig_d, sig_srst);
+ } else {
+ if (pol_srst)
+ sig_d = module->MuxGate(NEW_ID, sig_d, val_srst[0], sig_srst);
+ else
+ sig_d = module->MuxGate(NEW_ID, val_srst[0], sig_d, sig_srst);
+ }
+ has_srst = false;
+}
+
+Cell *FfData::emit() {
+ remove();
+ if (!width)
+ return nullptr;
+ if (!has_aload && !has_clk && !has_gclk && !has_sr) {
+ if (has_arst) {
+ // Convert this case to a D latch.
+ arst_to_aload();
+ } else {
+ // No control inputs left. Turn into a const driver.
+ module->connect(sig_q, val_init);
+ return nullptr;
+ }
+ }
+ if (initvals)
+ initvals->set_init(sig_q, val_init);
+ if (!is_fine) {
+ if (has_gclk) {
+ log_assert(!has_clk);
+ log_assert(!has_ce);
+ log_assert(!has_aload);
+ log_assert(!has_arst);
+ log_assert(!has_srst);
+ log_assert(!has_sr);
+ cell = module->addFf(name, sig_d, sig_q);
+ } else if (!has_aload && !has_clk) {
+ log_assert(has_sr);
+ cell = module->addSr(name, sig_set, sig_clr, sig_q, pol_set, pol_clr);
+ } else if (!has_clk) {
+ log_assert(!has_srst);
+ if (has_sr)
+ cell = module->addDlatchsr(name, sig_aload, sig_set, sig_clr, sig_ad, sig_q, pol_aload, pol_set, pol_clr);
+ else if (has_arst)
+ cell = module->addAdlatch(name, sig_aload, sig_arst, sig_ad, sig_q, val_arst, pol_aload, pol_arst);
+ else
+ cell = module->addDlatch(name, sig_aload, sig_ad, sig_q, pol_aload);
+ } else {
+ if (has_sr) {
+ if (has_ce)
+ cell = module->addDffsre(name, sig_clk, sig_ce, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_ce, pol_set, pol_clr);
+ else
+ cell = module->addDffsr(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr);
+ } else if (has_arst) {
+ if (has_ce)
+ cell = module->addAdffe(name, sig_clk, sig_ce, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_ce, pol_arst);
+ else
+ cell = module->addAdff(name, sig_clk, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_arst);
+ } else if (has_aload) {
+ if (has_ce)
+ cell = module->addAldffe(name, sig_clk, sig_ce, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_ce, pol_aload);
+ else
+ cell = module->addAldff(name, sig_clk, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_aload);
+ } else if (has_srst) {
+ if (has_ce)
+ if (ce_over_srst)
+ cell = module->addSdffce(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_ce, pol_srst);
+ else
+ cell = module->addSdffe(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_ce, pol_srst);
+ else
+ cell = module->addSdff(name, sig_clk, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_srst);
+ } else {
+ if (has_ce)
+ cell = module->addDffe(name, sig_clk, sig_ce, sig_d, sig_q, pol_clk, pol_ce);
+ else
+ cell = module->addDff(name, sig_clk, sig_d, sig_q, pol_clk);
+ }
+ }
+ } else {
+ if (has_gclk) {
+ log_assert(!has_clk);
+ log_assert(!has_ce);
+ log_assert(!has_aload);
+ log_assert(!has_arst);
+ log_assert(!has_srst);
+ log_assert(!has_sr);
+ cell = module->addFfGate(name, sig_d, sig_q);
+ } else if (!has_aload && !has_clk) {
+ log_assert(has_sr);
+ cell = module->addSrGate(name, sig_set, sig_clr, sig_q, pol_set, pol_clr);
+ } else if (!has_clk) {
+ log_assert(!has_srst);
+ if (has_sr)
+ cell = module->addDlatchsrGate(name, sig_aload, sig_set, sig_clr, sig_ad, sig_q, pol_aload, pol_set, pol_clr);
+ else if (has_arst)
+ cell = module->addAdlatchGate(name, sig_aload, sig_arst, sig_ad, sig_q, val_arst.as_bool(), pol_aload, pol_arst);
+ else
+ cell = module->addDlatchGate(name, sig_aload, sig_ad, sig_q, pol_aload);
+ } else {
+ if (has_sr) {
+ if (has_ce)
+ cell = module->addDffsreGate(name, sig_clk, sig_ce, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_ce, pol_set, pol_clr);
+ else
+ cell = module->addDffsrGate(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr);
+ } else if (has_arst) {
+ if (has_ce)
+ cell = module->addAdffeGate(name, sig_clk, sig_ce, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_ce, pol_arst);
+ else
+ cell = module->addAdffGate(name, sig_clk, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_arst);
+ } else if (has_aload) {
+ if (has_ce)
+ cell = module->addAldffeGate(name, sig_clk, sig_ce, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_ce, pol_aload);
+ else
+ cell = module->addAldffGate(name, sig_clk, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_aload);
+ } else if (has_srst) {
+ if (has_ce)
+ if (ce_over_srst)
+ cell = module->addSdffceGate(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_ce, pol_srst);
+ else
+ cell = module->addSdffeGate(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_ce, pol_srst);
+ else
+ cell = module->addSdffGate(name, sig_clk, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_srst);
+ } else {
+ if (has_ce)
+ cell = module->addDffeGate(name, sig_clk, sig_ce, sig_d, sig_q, pol_clk, pol_ce);
+ else
+ cell = module->addDffGate(name, sig_clk, sig_d, sig_q, pol_clk);
+ }
+ }
+ }
+ cell->attributes = attributes;
+ return cell;
+}
+
+void FfData::remove() {
+ if (cell) {
+ remove_init();
+ module->remove(cell);
+ cell = nullptr;
+ }
+}
+
+namespace {
+ State invert(State s) {
+ switch (s) {
+ case State::S0: return State::S1;
+ case State::S1: return State::S0;
+ default: return s;
+ }
+ }
+}
+
+void FfData::flip_bits(const pool<int> &bits) {
+ if (!bits.size())
+ return;
+
+ remove_init();
+
+ Wire *new_q = module->addWire(NEW_ID, width);
+
+ for (auto bit: bits) {
+ if (has_arst)
+ val_arst[bit] = invert(val_arst[bit]);
+ if (has_srst)
+ val_srst[bit] = invert(val_srst[bit]);
+ val_init[bit] = invert(val_init[bit]);
+ }
+
+ if (has_sr && cell) {
+ log_warning("Flipping D/Q/init and inserting priority fixup to legalize %s.%s [%s].\n", log_id(module->name), log_id(cell->name), log_id(cell->type));
+ }
+
+ if (is_fine) {
+ if (has_sr) {
+ bool new_pol_clr = pol_set;
+ SigSpec new_sig_clr;
+ if (pol_set) {
+ if (pol_clr) {
+ new_sig_clr = module->AndnotGate(NEW_ID, sig_set, sig_clr);
+ } else {
+ new_sig_clr = module->AndGate(NEW_ID, sig_set, sig_clr);
+ }
+ } else {
+ if (pol_clr) {
+ new_sig_clr = module->OrGate(NEW_ID, sig_set, sig_clr);
+ } else {
+ new_sig_clr = module->OrnotGate(NEW_ID, sig_set, sig_clr);
+ }
+ }
+ pol_set = pol_clr;
+ sig_set = sig_clr;
+ pol_clr = new_pol_clr;
+ sig_clr = new_sig_clr;
+ }
+ if (has_clk || has_gclk)
+ sig_d = module->NotGate(NEW_ID, sig_d);
+ if (has_aload)
+ sig_ad = module->NotGate(NEW_ID, sig_ad);
+ module->addNotGate(NEW_ID, new_q, sig_q);
+ }
+ else
+ {
+ if (has_sr) {
+ SigSpec not_clr;
+ if (!pol_clr) {
+ not_clr = sig_clr;
+ sig_clr = module->Not(NEW_ID, sig_clr);
+ pol_clr = true;
+ } else {
+ not_clr = module->Not(NEW_ID, sig_clr);
+ }
+ if (!pol_set) {
+ sig_set = module->Not(NEW_ID, sig_set);
+ pol_set = true;
+ }
+
+ SigSpec masked_set = module->And(NEW_ID, sig_set, not_clr);
+ for (auto bit: bits) {
+ sig_set[bit] = sig_clr[bit];
+ sig_clr[bit] = masked_set[bit];
+ }
+ }
+
+ Const mask = Const(State::S0, width);
+ for (auto bit: bits)
+ mask.bits[bit] = State::S1;
+
+ if (has_clk || has_gclk)
+ sig_d = module->Xor(NEW_ID, sig_d, mask);
+ if (has_aload)
+ sig_ad = module->Xor(NEW_ID, sig_ad, mask);
+ module->addXor(NEW_ID, new_q, mask, sig_q);
+ }
+
+ sig_q = new_q;
+}
diff --git a/kernel/ff.h b/kernel/ff.h
index 0aecbaa2a..5a629d5dd 100644
--- a/kernel/ff.h
+++ b/kernel/ff.h
@@ -25,460 +25,190 @@
YOSYS_NAMESPACE_BEGIN
+// Describes a flip-flop or a latch.
+//
+// If has_gclk, this is a formal verification FF with implicit global clock:
+// Q is simply previous cycle's D.
+//
+// Otherwise, the FF/latch can have any number of features selected by has_*
+// attributes that determine Q's value (in order of decreasing priority):
+//
+// - on start, register is initialized to val_init
+// - if has_sr is present:
+// - sig_clr is per-bit async clear, and sets the corresponding bit to 0
+// if active
+// - sig_set is per-bit async set, and sets the corresponding bit to 1
+// if active
+// - if has_arst is present:
+// - sig_arst is whole-reg async reset, and sets the whole register to val_arst
+// - if has_aload is present:
+// - sig_aload is whole-reg async load (aka latch gate enable), and sets the whole
+// register to sig_ad
+// - if has_clk is present, and we're currently on a clock edge:
+// - if has_ce is present and ce_over_srst is true:
+// - ignore clock edge (don't change value) unless sig_ce is active
+// - if has_srst is present:
+// - sig_srst is whole-reg sync reset and sets the register to val_srst
+// - if has_ce is present and ce_over_srst is false:
+// - ignore clock edge (don't change value) unless sig_ce is active
+// - set whole reg to sig_d
+// - if nothing of the above applies, the reg value remains unchanged
+//
+// Since the yosys FF cell library isn't fully generic, not all combinations
+// of the features above can be supported:
+//
+// - only one of has_srst, has_arst, has_sr can be used
+// - if has_clk is used together with has_aload, then has_srst, has_arst,
+// has_sr cannot be used
+//
+// The valid feature combinations are thus:
+//
+// - has_clk + optional has_ce [dff/dffe]
+// - has_clk + optional has_ce + has_arst [adff/adffe]
+// - has_clk + optional has_ce + has_aload [aldff/aldffe]
+// - has_clk + optional has_ce + has_sr [dffsr/dffsre]
+// - has_clk + optional has_ce + has_srst [sdff/sdffe/sdffce]
+// - has_aload [dlatch]
+// - has_aload + has_arst [adlatch]
+// - has_aload + has_sr [dlatchsr]
+// - has_sr [sr]
+// - has_arst [does not correspond to a native cell, represented as dlatch with const D input]
+// - empty set [not a cell — will be emitted as a simple direct connection]
+
struct FfData {
+ Module *module;
FfInitVals *initvals;
+ Cell *cell;
+ IdString name;
+ // The FF output.
SigSpec sig_q;
+ // The sync data input, present if has_clk or has_gclk.
SigSpec sig_d;
+ // The async data input, present if has_aload.
+ SigSpec sig_ad;
+ // The sync clock, present if has_clk.
SigSpec sig_clk;
- SigSpec sig_en;
+ // The clock enable, present if has_ce.
+ SigSpec sig_ce;
+ // The async load enable, present if has_aload.
+ SigSpec sig_aload;
+ // The async reset, preset if has_arst.
SigSpec sig_arst;
+ // The sync reset, preset if has_srst.
SigSpec sig_srst;
+ // The async clear (per-lane), present if has_sr.
SigSpec sig_clr;
+ // The async set (per-lane), present if has_sr.
SigSpec sig_set;
- bool has_d;
+ // True if this is a clocked (edge-sensitive) flip-flop.
bool has_clk;
- bool has_en;
+ // True if this is a $ff, exclusive with every other has_*.
+ bool has_gclk;
+ // True if this FF has a clock enable. Depends on has_clk.
+ bool has_ce;
+ // True if this FF has async load function — this includes D latches.
+ // If this and has_clk are both set, has_arst and has_sr cannot be set.
+ bool has_aload;
+ // True if this FF has sync set/reset. Depends on has_clk, exclusive
+ // with has_arst, has_sr, has_aload.
bool has_srst;
+ // True if this FF has async set/reset. Exclusive with has_srst,
+ // has_sr. If this and has_clk are both set, has_aload cannot be set.
bool has_arst;
+ // True if this FF has per-bit async set + clear. Exclusive with
+ // has_srst, has_arst. If this and has_clk are both set, has_aload
+ // cannot be set.
bool has_sr;
+ // If has_ce and has_srst are both set, determines their relative
+ // priorities: if true, inactive ce disables srst; if false, srst
+ // operates independent of ce.
bool ce_over_srst;
+ // True if this FF is a fine cell, false if it is a coarse cell.
+ // If true, width must be 1.
bool is_fine;
+ // Polarities, corresponding to sig_*. True means active-high, false
+ // means active-low.
bool pol_clk;
- bool pol_en;
+ bool pol_ce;
+ bool pol_aload;
bool pol_arst;
bool pol_srst;
bool pol_clr;
bool pol_set;
+ // The value loaded by sig_arst.
Const val_arst;
+ // The value loaded by sig_srst.
Const val_srst;
+ // The initial value at power-up.
Const val_init;
- Const val_d;
- bool d_is_const;
+ // The FF data width in bits.
int width;
dict<IdString, Const> attributes;
- FfData(FfInitVals *initvals, Cell *cell = nullptr) : initvals(initvals) {
+ FfData(Module *module = nullptr, FfInitVals *initvals = nullptr, IdString name = IdString()) : module(module), initvals(initvals), cell(nullptr), name(name) {
width = 0;
- has_d = true;
has_clk = false;
- has_en = false;
+ has_gclk = false;
+ has_ce = false;
+ has_aload = false;
has_srst = false;
has_arst = false;
has_sr = false;
ce_over_srst = false;
is_fine = false;
pol_clk = false;
- pol_en = false;
+ pol_aload = false;
+ pol_ce = false;
pol_arst = false;
pol_srst = false;
pol_clr = false;
pol_set = false;
- d_is_const = false;
+ }
- if (!cell)
- return;
+ FfData(FfInitVals *initvals, Cell *cell_);
- sig_q = cell->getPort(ID::Q);
- width = GetSize(sig_q);
- attributes = cell->attributes;
+ // Returns a FF identical to this one, but only keeping bit indices from the argument.
+ FfData slice(const std::vector<int> &bits);
- if (initvals)
- val_init = (*initvals)(sig_q);
+ void add_dummy_ce();
+ void add_dummy_srst();
+ void add_dummy_arst();
+ void add_dummy_aload();
+ void add_dummy_sr();
+ void add_dummy_clk();
- std::string type_str = cell->type.str();
+ void arst_to_aload();
+ void arst_to_sr();
- if (cell->type.in(ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) {
- if (cell->type == ID($sr)) {
- has_d = false;
- } else {
- sig_d = cell->getPort(ID::D);
- }
- if (!cell->type.in(ID($ff), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) {
- has_clk = true;
- sig_clk = cell->getPort(ID::CLK);
- pol_clk = cell->getParam(ID::CLK_POLARITY).as_bool();
- }
- if (cell->type.in(ID($dffe), ID($dffsre), ID($adffe), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr))) {
- has_en = true;
- sig_en = cell->getPort(ID::EN);
- pol_en = cell->getParam(ID::EN_POLARITY).as_bool();
- }
- if (cell->type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) {
- has_sr = true;
- sig_clr = cell->getPort(ID::CLR);
- sig_set = cell->getPort(ID::SET);
- pol_clr = cell->getParam(ID::CLR_POLARITY).as_bool();
- pol_set = cell->getParam(ID::SET_POLARITY).as_bool();
- }
- if (cell->type.in(ID($adff), ID($adffe), ID($adlatch))) {
- has_arst = true;
- sig_arst = cell->getPort(ID::ARST);
- pol_arst = cell->getParam(ID::ARST_POLARITY).as_bool();
- val_arst = cell->getParam(ID::ARST_VALUE);
- }
- if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) {
- has_srst = true;
- sig_srst = cell->getPort(ID::SRST);
- pol_srst = cell->getParam(ID::SRST_POLARITY).as_bool();
- val_srst = cell->getParam(ID::SRST_VALUE);
- ce_over_srst = cell->type == ID($sdffce);
- }
- } else if (cell->type == ID($_FF_)) {
- is_fine = true;
- sig_d = cell->getPort(ID::D);
- } else if (type_str.substr(0, 5) == "$_SR_") {
- is_fine = true;
- has_d = false;
- has_sr = true;
- pol_set = type_str[5] == 'P';
- pol_clr = type_str[6] == 'P';
- sig_set = cell->getPort(ID::S);
- sig_clr = cell->getPort(ID::R);
- } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 8) {
- is_fine = true;
- sig_d = cell->getPort(ID::D);
- has_clk = true;
- pol_clk = type_str[6] == 'P';
- sig_clk = cell->getPort(ID::C);
- } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 10) {
- is_fine = true;
- sig_d = cell->getPort(ID::D);
- has_clk = true;
- pol_clk = type_str[7] == 'P';
- sig_clk = cell->getPort(ID::C);
- has_en = true;
- pol_en = type_str[8] == 'P';
- sig_en = cell->getPort(ID::E);
- } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) {
- is_fine = true;
- sig_d = cell->getPort(ID::D);
- has_clk = true;
- pol_clk = type_str[6] == 'P';
- sig_clk = cell->getPort(ID::C);
- has_arst = true;
- pol_arst = type_str[7] == 'P';
- sig_arst = cell->getPort(ID::R);
- val_arst = type_str[8] == '1' ? State::S1 : State::S0;
- } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 12) {
- is_fine = true;
- sig_d = cell->getPort(ID::D);
- has_clk = true;
- pol_clk = type_str[7] == 'P';
- sig_clk = cell->getPort(ID::C);
- has_arst = true;
- pol_arst = type_str[8] == 'P';
- sig_arst = cell->getPort(ID::R);
- val_arst = type_str[9] == '1' ? State::S1 : State::S0;
- has_en = true;
- pol_en = type_str[10] == 'P';
- sig_en = cell->getPort(ID::E);
- } else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) {
- is_fine = true;
- sig_d = cell->getPort(ID::D);
- has_clk = true;
- pol_clk = type_str[8] == 'P';
- sig_clk = cell->getPort(ID::C);
- has_sr = true;
- pol_set = type_str[9] == 'P';
- pol_clr = type_str[10] == 'P';
- sig_set = cell->getPort(ID::S);
- sig_clr = cell->getPort(ID::R);
- } else if (type_str.substr(0, 9) == "$_DFFSRE_" && type_str.size() == 14) {
- is_fine = true;
- sig_d = cell->getPort(ID::D);
- has_clk = true;
- pol_clk = type_str[9] == 'P';
- sig_clk = cell->getPort(ID::C);
- has_sr = true;
- pol_set = type_str[10] == 'P';
- pol_clr = type_str[11] == 'P';
- sig_set = cell->getPort(ID::S);
- sig_clr = cell->getPort(ID::R);
- has_en = true;
- pol_en = type_str[12] == 'P';
- sig_en = cell->getPort(ID::E);
- } else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) {
- is_fine = true;
- sig_d = cell->getPort(ID::D);
- has_clk = true;
- pol_clk = type_str[7] == 'P';
- sig_clk = cell->getPort(ID::C);
- has_srst = true;
- pol_srst = type_str[8] == 'P';
- sig_srst = cell->getPort(ID::R);
- val_srst = type_str[9] == '1' ? State::S1 : State::S0;
- } else if (type_str.substr(0, 8) == "$_SDFFE_" && type_str.size() == 13) {
- is_fine = true;
- sig_d = cell->getPort(ID::D);
- has_clk = true;
- pol_clk = type_str[8] == 'P';
- sig_clk = cell->getPort(ID::C);
- has_srst = true;
- pol_srst = type_str[9] == 'P';
- sig_srst = cell->getPort(ID::R);
- val_srst = type_str[10] == '1' ? State::S1 : State::S0;
- has_en = true;
- pol_en = type_str[11] == 'P';
- sig_en = cell->getPort(ID::E);
- } else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) {
- is_fine = true;
- sig_d = cell->getPort(ID::D);
- has_clk = true;
- pol_clk = type_str[9] == 'P';
- sig_clk = cell->getPort(ID::C);
- has_srst = true;
- pol_srst = type_str[10] == 'P';
- sig_srst = cell->getPort(ID::R);
- val_srst = type_str[11] == '1' ? State::S1 : State::S0;
- has_en = true;
- pol_en = type_str[12] == 'P';
- sig_en = cell->getPort(ID::E);
- ce_over_srst = true;
- } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) {
- is_fine = true;
- sig_d = cell->getPort(ID::D);
- has_en = true;
- pol_en = type_str[9] == 'P';
- sig_en = cell->getPort(ID::E);
- } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) {
- is_fine = true;
- sig_d = cell->getPort(ID::D);
- has_en = true;
- pol_en = type_str[9] == 'P';
- sig_en = cell->getPort(ID::E);
- has_arst = true;
- pol_arst = type_str[10] == 'P';
- sig_arst = cell->getPort(ID::R);
- val_arst = type_str[11] == '1' ? State::S1 : State::S0;
- } else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) {
- is_fine = true;
- sig_d = cell->getPort(ID::D);
- has_en = true;
- pol_en = type_str[11] == 'P';
- sig_en = cell->getPort(ID::E);
- has_sr = true;
- pol_set = type_str[12] == 'P';
- pol_clr = type_str[13] == 'P';
- sig_set = cell->getPort(ID::S);
- sig_clr = cell->getPort(ID::R);
- } else {
- log_assert(0);
- }
- if (has_d && sig_d.is_fully_const()) {
- d_is_const = true;
- val_d = sig_d.as_const();
- if (has_en && !has_clk && !has_sr && !has_arst) {
- // Plain D latches with const D treated specially.
- has_en = has_d = false;
- has_arst = true;
- sig_arst = sig_en;
- pol_arst = pol_en;
- val_arst = val_d;
- }
- }
- }
+ void aload_to_sr();
- // Returns a FF identical to this one, but only keeping bit indices from the argument.
- FfData slice(const std::vector<int> &bits) {
- FfData res(initvals);
- res.sig_clk = sig_clk;
- res.sig_en = sig_en;
- res.sig_arst = sig_arst;
- res.sig_srst = sig_srst;
- res.has_d = has_d;
- res.has_clk = has_clk;
- res.has_en = has_en;
- res.has_arst = has_arst;
- res.has_srst = has_srst;
- res.has_sr = has_sr;
- res.ce_over_srst = ce_over_srst;
- res.is_fine = is_fine;
- res.pol_clk = pol_clk;
- res.pol_en = pol_en;
- res.pol_arst = pol_arst;
- res.pol_srst = pol_srst;
- res.pol_clr = pol_clr;
- res.pol_set = pol_set;
- res.attributes = attributes;
- for (int i : bits) {
- res.sig_q.append(sig_q[i]);
- if (has_d)
- res.sig_d.append(sig_d[i]);
- if (has_sr) {
- res.sig_clr.append(sig_clr[i]);
- res.sig_set.append(sig_set[i]);
- }
- if (has_arst)
- res.val_arst.bits.push_back(val_arst[i]);
- if (has_srst)
- res.val_srst.bits.push_back(val_srst[i]);
- res.val_init.bits.push_back(val_init[i]);
- }
- res.width = GetSize(res.sig_q);
- // Slicing bits out may cause D to become const.
- if (has_d && res.sig_d.is_fully_const()) {
- res.d_is_const = true;
- res.val_d = res.sig_d.as_const();
- }
- return res;
- }
+ // Given a FF with both has_ce and has_srst, sets ce_over_srst to the given value and
+ // fixes up control signals appropriately to preserve semantics.
+ void convert_ce_over_srst(bool val);
- void unmap_ce(Module *module) {
- if (!has_en)
- return;
- log_assert(has_clk);
- if (has_srst && ce_over_srst)
- unmap_srst(module);
+ void unmap_ce();
+ void unmap_srst();
- if (!is_fine) {
- if (pol_en)
- sig_d = module->Mux(NEW_ID, sig_q, sig_d, sig_en);
- else
- sig_d = module->Mux(NEW_ID, sig_d, sig_q, sig_en);
- } else {
- if (pol_en)
- sig_d = module->MuxGate(NEW_ID, sig_q, sig_d, sig_en);
- else
- sig_d = module->MuxGate(NEW_ID, sig_d, sig_q, sig_en);
- }
- has_en = false;
+ void unmap_ce_srst() {
+ unmap_ce();
+ unmap_srst();
}
- void unmap_srst(Module *module) {
- if (!has_srst)
- return;
- if (has_en && !ce_over_srst)
- unmap_ce(module);
+ Cell *emit();
- if (!is_fine) {
- if (pol_srst)
- sig_d = module->Mux(NEW_ID, sig_d, val_srst, sig_srst);
- else
- sig_d = module->Mux(NEW_ID, val_srst, sig_d, sig_srst);
- } else {
- if (pol_srst)
- sig_d = module->MuxGate(NEW_ID, sig_d, val_srst[0], sig_srst);
- else
- sig_d = module->MuxGate(NEW_ID, val_srst[0], sig_d, sig_srst);
- }
- has_srst = false;
+ // Removes init attribute from the Q output, but keeps val_init unchanged.
+ // It will be automatically reattached on emit. Use this before changing sig_q.
+ void remove_init() {
+ if (initvals)
+ initvals->remove_init(sig_q);
}
- void unmap_ce_srst(Module *module) {
- unmap_ce(module);
- unmap_srst(module);
- }
+ void remove();
- Cell *emit(Module *module, IdString name) {
- if (!width)
- return nullptr;
- if (!has_d && !has_sr) {
- if (has_arst) {
- // Convert this case to a D latch.
- has_d = has_en = true;
- has_arst = false;
- sig_d = val_arst;
- sig_en = sig_arst;
- pol_en = pol_arst;
- } else {
- // No control inputs left. Turn into a const driver.
- initvals->remove_init(sig_q);
- module->connect(sig_q, val_init);
- return nullptr;
- }
- }
- initvals->set_init(sig_q, val_init);
- Cell *cell;
- if (!is_fine) {
- if (!has_d) {
- log_assert(has_sr);
- cell = module->addSr(name, sig_set, sig_clr, sig_q, pol_set, pol_clr);
- } else if (!has_clk && !has_en) {
- log_assert(!has_arst);
- log_assert(!has_srst);
- log_assert(!has_sr);
- cell = module->addFf(name, sig_d, sig_q);
- } else if (!has_clk) {
- log_assert(!has_srst);
- if (has_sr)
- cell = module->addDlatchsr(name, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_en, pol_set, pol_clr);
- else if (has_arst)
- cell = module->addAdlatch(name, sig_en, sig_arst, sig_d, sig_q, val_arst, pol_en, pol_arst);
- else
- cell = module->addDlatch(name, sig_en, sig_d, sig_q, pol_en);
- } else {
- if (has_sr) {
- if (has_en)
- cell = module->addDffsre(name, sig_clk, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_en, pol_set, pol_clr);
- else
- cell = module->addDffsr(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr);
- } else if (has_arst) {
- if (has_en)
- cell = module->addAdffe(name, sig_clk, sig_en, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_en, pol_arst);
- else
- cell = module->addAdff(name, sig_clk, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_arst);
- } else if (has_srst) {
- if (has_en)
- if (ce_over_srst)
- cell = module->addSdffce(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_en, pol_srst);
- else
- cell = module->addSdffe(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_en, pol_srst);
- else
- cell = module->addSdff(name, sig_clk, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_srst);
- } else {
- if (has_en)
- cell = module->addDffe(name, sig_clk, sig_en, sig_d, sig_q, pol_clk, pol_en);
- else
- cell = module->addDff(name, sig_clk, sig_d, sig_q, pol_clk);
- }
- }
- } else {
- if (!has_d) {
- log_assert(has_sr);
- cell = module->addSrGate(name, sig_set, sig_clr, sig_q, pol_set, pol_clr);
- } else if (!has_clk && !has_en) {
- log_assert(!has_arst);
- log_assert(!has_srst);
- log_assert(!has_sr);
- cell = module->addFfGate(name, sig_d, sig_q);
- } else if (!has_clk) {
- log_assert(!has_srst);
- if (has_sr)
- cell = module->addDlatchsrGate(name, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_en, pol_set, pol_clr);
- else if (has_arst)
- cell = module->addAdlatchGate(name, sig_en, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_en, pol_arst);
- else
- cell = module->addDlatchGate(name, sig_en, sig_d, sig_q, pol_en);
- } else {
- if (has_sr) {
- if (has_en)
- cell = module->addDffsreGate(name, sig_clk, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_en, pol_set, pol_clr);
- else
- cell = module->addDffsrGate(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr);
- } else if (has_arst) {
- if (has_en)
- cell = module->addAdffeGate(name, sig_clk, sig_en, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_en, pol_arst);
- else
- cell = module->addAdffGate(name, sig_clk, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_arst);
- } else if (has_srst) {
- if (has_en)
- if (ce_over_srst)
- cell = module->addSdffceGate(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_en, pol_srst);
- else
- cell = module->addSdffeGate(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_en, pol_srst);
- else
- cell = module->addSdffGate(name, sig_clk, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_srst);
- } else {
- if (has_en)
- cell = module->addDffeGate(name, sig_clk, sig_en, sig_d, sig_q, pol_clk, pol_en);
- else
- cell = module->addDffGate(name, sig_clk, sig_d, sig_q, pol_clk);
- }
- }
- }
- cell->attributes = attributes;
- return cell;
- }
+ // Flip the sense of the given bit slices of the FF: insert inverters on data
+ // inputs and output, flip the corresponding init/reset bits, swap clr/set
+ // inputs with proper priority fix.
+ void flip_bits(const pool<int> &bits);
};
YOSYS_NAMESPACE_END
diff --git a/kernel/ffinit.h b/kernel/ffinit.h
index 025b0c862..9d33ac572 100644
--- a/kernel/ffinit.h
+++ b/kernel/ffinit.h
@@ -28,7 +28,6 @@ YOSYS_NAMESPACE_BEGIN
struct FfInitVals
{
const SigMap *sigmap;
- RTLIL::Module *module;
dict<SigBit, std::pair<State,SigBit>> initbits;
void set(const SigMap *sigmap_, RTLIL::Module *module)
diff --git a/kernel/ffmerge.cc b/kernel/ffmerge.cc
new file mode 100644
index 000000000..c65108413
--- /dev/null
+++ b/kernel/ffmerge.cc
@@ -0,0 +1,359 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2021 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/ffmerge.h"
+
+USING_YOSYS_NAMESPACE
+
+bool FfMergeHelper::is_output_unused(RTLIL::SigSpec sig) {
+ for (auto bit : (*sigmap)(sig))
+ if (sigbit_users_count[bit] != 0)
+ return false;
+ return true;
+}
+
+bool FfMergeHelper::find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair<Cell *, int>> &bits) {
+ ff = FfData(module, initvals, NEW_ID);
+ sigmap->apply(sig);
+
+ bool found = false;
+
+ for (auto bit : sig)
+ {
+ if (bit.wire == NULL || sigbit_users_count[bit] == 0) {
+ ff.width++;
+ ff.sig_q.append(bit);
+ ff.sig_d.append(bit);
+ ff.sig_clr.append(State::Sx);
+ ff.sig_set.append(State::Sx);
+ ff.val_init.bits.push_back(State::Sx);
+ ff.val_srst.bits.push_back(State::Sx);
+ ff.val_arst.bits.push_back(State::Sx);
+ continue;
+ }
+
+ if (sigbit_users_count[bit] != 1)
+ return false;
+
+ auto &sinks = dff_sink[bit];
+ if (sinks.size() != 1)
+ return false;
+
+ Cell *cell;
+ int idx;
+ std::tie(cell, idx) = *sinks.begin();
+ bits.insert(std::make_pair(cell, idx));
+
+ FfData cur_ff(initvals, cell);
+
+ // Reject latches and $ff.
+ if (!cur_ff.has_clk)
+ return false;
+
+ log_assert((*sigmap)(cur_ff.sig_d[idx]) == bit);
+
+ if (!found) {
+ ff.sig_clk = cur_ff.sig_clk;
+ ff.sig_ce = cur_ff.sig_ce;
+ ff.sig_aload = cur_ff.sig_aload;
+ ff.sig_srst = cur_ff.sig_srst;
+ ff.sig_arst = cur_ff.sig_arst;
+ ff.has_clk = cur_ff.has_clk;
+ ff.has_ce = cur_ff.has_ce;
+ ff.has_aload = cur_ff.has_aload;
+ ff.has_srst = cur_ff.has_srst;
+ ff.has_arst = cur_ff.has_arst;
+ ff.has_sr = cur_ff.has_sr;
+ ff.ce_over_srst = cur_ff.ce_over_srst;
+ ff.pol_clk = cur_ff.pol_clk;
+ ff.pol_ce = cur_ff.pol_ce;
+ ff.pol_aload = cur_ff.pol_aload;
+ ff.pol_arst = cur_ff.pol_arst;
+ ff.pol_srst = cur_ff.pol_srst;
+ ff.pol_clr = cur_ff.pol_clr;
+ ff.pol_set = cur_ff.pol_set;
+ } else {
+ if (ff.has_clk != cur_ff.has_clk)
+ return false;
+ if (ff.has_ce != cur_ff.has_ce)
+ return false;
+ if (ff.has_aload != cur_ff.has_aload)
+ return false;
+ if (ff.has_srst != cur_ff.has_srst)
+ return false;
+ if (ff.has_arst != cur_ff.has_arst)
+ return false;
+ if (ff.has_sr != cur_ff.has_sr)
+ return false;
+ if (ff.has_clk) {
+ if (ff.sig_clk != cur_ff.sig_clk)
+ return false;
+ if (ff.pol_clk != cur_ff.pol_clk)
+ return false;
+ }
+ if (ff.has_ce) {
+ if (ff.sig_ce != cur_ff.sig_ce)
+ return false;
+ if (ff.pol_ce != cur_ff.pol_ce)
+ return false;
+ }
+ if (ff.has_aload) {
+ if (ff.sig_aload != cur_ff.sig_aload)
+ return false;
+ if (ff.pol_aload != cur_ff.pol_aload)
+ return false;
+ }
+ if (ff.has_srst) {
+ if (ff.sig_srst != cur_ff.sig_srst)
+ return false;
+ if (ff.pol_srst != cur_ff.pol_srst)
+ return false;
+ if (ff.has_ce && ff.ce_over_srst != cur_ff.ce_over_srst)
+ return false;
+ }
+ if (ff.has_arst) {
+ if (ff.sig_arst != cur_ff.sig_arst)
+ return false;
+ if (ff.pol_arst != cur_ff.pol_arst)
+ return false;
+ }
+ if (ff.has_sr) {
+ if (ff.pol_clr != cur_ff.pol_clr)
+ return false;
+ if (ff.pol_set != cur_ff.pol_set)
+ return false;
+ }
+ }
+
+ ff.width++;
+ ff.sig_d.append(cur_ff.sig_d[idx]);
+ ff.sig_ad.append(ff.has_aload ? cur_ff.sig_ad[idx] : State::Sx);
+ ff.sig_q.append(cur_ff.sig_q[idx]);
+ ff.sig_clr.append(ff.has_sr ? cur_ff.sig_clr[idx] : State::S0);
+ ff.sig_set.append(ff.has_sr ? cur_ff.sig_set[idx] : State::S0);
+ ff.val_arst.bits.push_back(ff.has_arst ? cur_ff.val_arst[idx] : State::Sx);
+ ff.val_srst.bits.push_back(ff.has_srst ? cur_ff.val_srst[idx] : State::Sx);
+ ff.val_init.bits.push_back(cur_ff.val_init[idx]);
+ found = true;
+ }
+
+ return found;
+}
+
+bool FfMergeHelper::find_input_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair<Cell *, int>> &bits) {
+ ff = FfData(module, initvals, NEW_ID);
+ sigmap->apply(sig);
+
+ bool found = false;
+
+ pool<int> const_bits;
+
+ for (auto bit : sig)
+ {
+ if (bit.wire == NULL) {
+ const_bits.insert(ff.width);
+ ff.width++;
+ ff.sig_q.append(bit);
+ ff.sig_d.append(bit);
+ // These two will be fixed up later.
+ ff.sig_clr.append(State::Sx);
+ ff.sig_set.append(State::Sx);
+ ff.val_init.bits.push_back(bit.data);
+ ff.val_srst.bits.push_back(bit.data);
+ ff.val_arst.bits.push_back(bit.data);
+ continue;
+ }
+
+ if (!dff_driver.count(bit))
+ return false;
+
+ Cell *cell;
+ int idx;
+ std::tie(cell, idx) = dff_driver[bit];
+ bits.insert(std::make_pair(cell, idx));
+
+ FfData cur_ff(initvals, cell);
+
+ log_assert((*sigmap)(cur_ff.sig_q[idx]) == bit);
+
+ if (!found) {
+ ff.sig_clk = cur_ff.sig_clk;
+ ff.sig_ce = cur_ff.sig_ce;
+ ff.sig_aload = cur_ff.sig_aload;
+ ff.sig_srst = cur_ff.sig_srst;
+ ff.sig_arst = cur_ff.sig_arst;
+ ff.has_clk = cur_ff.has_clk;
+ ff.has_gclk = cur_ff.has_gclk;
+ ff.has_ce = cur_ff.has_ce;
+ ff.has_aload = cur_ff.has_aload;
+ ff.has_srst = cur_ff.has_srst;
+ ff.has_arst = cur_ff.has_arst;
+ ff.has_sr = cur_ff.has_sr;
+ ff.ce_over_srst = cur_ff.ce_over_srst;
+ ff.pol_clk = cur_ff.pol_clk;
+ ff.pol_ce = cur_ff.pol_ce;
+ ff.pol_aload = cur_ff.pol_aload;
+ ff.pol_arst = cur_ff.pol_arst;
+ ff.pol_srst = cur_ff.pol_srst;
+ ff.pol_clr = cur_ff.pol_clr;
+ ff.pol_set = cur_ff.pol_set;
+ } else {
+ if (ff.has_gclk != cur_ff.has_gclk)
+ return false;
+ if (ff.has_clk != cur_ff.has_clk)
+ return false;
+ if (ff.has_ce != cur_ff.has_ce)
+ return false;
+ if (ff.has_aload != cur_ff.has_aload)
+ return false;
+ if (ff.has_srst != cur_ff.has_srst)
+ return false;
+ if (ff.has_arst != cur_ff.has_arst)
+ return false;
+ if (ff.has_sr != cur_ff.has_sr)
+ return false;
+ if (ff.has_clk) {
+ if (ff.sig_clk != cur_ff.sig_clk)
+ return false;
+ if (ff.pol_clk != cur_ff.pol_clk)
+ return false;
+ }
+ if (ff.has_ce) {
+ if (ff.sig_ce != cur_ff.sig_ce)
+ return false;
+ if (ff.pol_ce != cur_ff.pol_ce)
+ return false;
+ }
+ if (ff.has_aload) {
+ if (ff.sig_aload != cur_ff.sig_aload)
+ return false;
+ if (ff.pol_aload != cur_ff.pol_aload)
+ return false;
+ }
+ if (ff.has_srst) {
+ if (ff.sig_srst != cur_ff.sig_srst)
+ return false;
+ if (ff.pol_srst != cur_ff.pol_srst)
+ return false;
+ if (ff.has_ce && ff.ce_over_srst != cur_ff.ce_over_srst)
+ return false;
+ }
+ if (ff.has_arst) {
+ if (ff.sig_arst != cur_ff.sig_arst)
+ return false;
+ if (ff.pol_arst != cur_ff.pol_arst)
+ return false;
+ }
+ if (ff.has_sr) {
+ if (ff.pol_clr != cur_ff.pol_clr)
+ return false;
+ if (ff.pol_set != cur_ff.pol_set)
+ return false;
+ }
+ }
+
+ ff.width++;
+ ff.sig_d.append((ff.has_clk || ff.has_gclk) ? cur_ff.sig_d[idx] : State::Sx);
+ ff.sig_ad.append(ff.has_aload ? cur_ff.sig_ad[idx] : State::Sx);
+ ff.sig_q.append(cur_ff.sig_q[idx]);
+ ff.sig_clr.append(ff.has_sr ? cur_ff.sig_clr[idx] : State::S0);
+ ff.sig_set.append(ff.has_sr ? cur_ff.sig_set[idx] : State::S0);
+ ff.val_arst.bits.push_back(ff.has_arst ? cur_ff.val_arst[idx] : State::Sx);
+ ff.val_srst.bits.push_back(ff.has_srst ? cur_ff.val_srst[idx] : State::Sx);
+ ff.val_init.bits.push_back(cur_ff.val_init[idx]);
+ found = true;
+ }
+
+ if (found && ff.has_sr) {
+ for (auto i: const_bits) {
+ if (ff.sig_d[i] == State::S0) {
+ ff.sig_set[i] = ff.pol_set ? State::S0 : State::S1;
+ } else if (ff.sig_d[i] == State::S1) {
+ ff.sig_clr[i] = ff.pol_clr ? State::S0 : State::S1;
+ }
+ }
+ }
+
+ return found;
+}
+
+
+void FfMergeHelper::remove_output_ff(const pool<std::pair<Cell *, int>> &bits) {
+ for (auto &it : bits) {
+ Cell *cell = it.first;
+ int idx = it.second;
+ SigSpec q = cell->getPort(ID::Q);
+ initvals->remove_init(q[idx]);
+ dff_driver.erase((*sigmap)(q[idx]));
+ q[idx] = module->addWire(stringf("$ffmerge_disconnected$%d", autoidx++));
+ cell->setPort(ID::Q, q);
+ }
+}
+
+void FfMergeHelper::mark_input_ff(const pool<std::pair<Cell *, int>> &bits) {
+ for (auto &it : bits) {
+ Cell *cell = it.first;
+ int idx = it.second;
+ if (cell->hasPort(ID::D)) {
+ SigSpec d = cell->getPort(ID::D);
+ // The user count was already at least 1
+ // (for the D port). Bump it as it is now connected
+ // to the merged-to cell as well. This suffices for
+ // it to not be considered for output merging.
+ sigbit_users_count[d[idx]]++;
+ }
+ }
+}
+
+void FfMergeHelper::set(FfInitVals *initvals_, RTLIL::Module *module_)
+{
+ clear();
+ initvals = initvals_;
+ sigmap = initvals->sigmap;
+ module = module_;
+
+ for (auto wire : module->wires()) {
+ if (wire->port_output)
+ for (auto bit : (*sigmap)(wire))
+ sigbit_users_count[bit]++;
+ }
+
+ for (auto cell : module->cells()) {
+ if (RTLIL::builtin_ff_cell_types().count(cell->type)) {
+ if (cell->hasPort(ID::D)) {
+ SigSpec d = (*sigmap)(cell->getPort(ID::D));
+ for (int i = 0; i < GetSize(d); i++)
+ dff_sink[d[i]].insert(std::make_pair(cell, i));
+ }
+ SigSpec q = (*sigmap)(cell->getPort(ID::Q));
+ for (int i = 0; i < GetSize(q); i++)
+ dff_driver[q[i]] = std::make_pair(cell, i);
+ }
+ for (auto &conn : cell->connections())
+ if (!cell->known() || cell->input(conn.first))
+ for (auto bit : (*sigmap)(conn.second))
+ sigbit_users_count[bit]++;
+ }
+}
+
+void FfMergeHelper::clear() {
+ dff_driver.clear();
+ dff_sink.clear();
+ sigbit_users_count.clear();
+}
diff --git a/kernel/ffmerge.h b/kernel/ffmerge.h
new file mode 100644
index 000000000..5428da324
--- /dev/null
+++ b/kernel/ffmerge.h
@@ -0,0 +1,141 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2021 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.
+ *
+ */
+
+#ifndef FFMERGE_H
+#define FFMERGE_H
+
+#include "kernel/ffinit.h"
+#include "kernel/ff.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+// A helper class for passes that want to merge FFs on the input or output
+// of a cell into the cell itself.
+//
+// The procedure is:
+//
+// 1. Construct this class (at beginning of processing for a given module).
+// 2. For every considered cell:
+//
+// a. Call find_output_ff for every considered output.
+// b. Call find_input_ff for every considered input.
+// c. Look at the FF description returned (if any) from each call, reject
+// results that cannot be merged into given cell for any reason.
+// If both inputs and outputs are being merged, take care of FF bits that
+// are returned in both input and output results (a FF bit cannot be
+// merged to both). Decide on the final set of FF bits to merge.
+// d. Call remove_output_ff for every find_output_ff result that will be used
+// for merging. This removes the actual FF bits from design and from index.
+// e. Call mark_input_ff for every find_input_ff result that will be used
+// for merging. This updates the index disallowing further usage of these
+// FF bits for output FF merging, if they were eligible before. The actual
+// FF bits are still left in the design and can be merged into other inputs.
+// If the FF bits are not otherwise used, they will be removed by later
+// opt passes.
+// f. Merge the FFs into the cell.
+//
+// Note that, if both inputs and outputs are being considered for merging in
+// a single pass, the result may be nondeterministic (depending on cell iteration
+// order) because a given FF bit could be eligible for both input and output merge,
+// perhaps in different cells. For this reason, it may be a good idea to separate
+// input and output merging.
+
+struct FfMergeHelper
+{
+ const SigMap *sigmap;
+ RTLIL::Module *module;
+ FfInitVals *initvals;
+
+ dict<SigBit, std::pair<Cell*, int>> dff_driver;
+ dict<SigBit, pool<std::pair<Cell*, int>>> dff_sink;
+ dict<SigBit, int> sigbit_users_count;
+
+ // Returns true if all bits in sig are completely unused.
+ bool is_output_unused(RTLIL::SigSpec sig);
+
+ // Finds the FF to merge into a given cell output. Takes sig, which
+ // is the current cell output — it will be the sig_d of the found FF.
+ // If found, returns true, and fills the two output arguments.
+ //
+ // For every bit of sig, this function finds a FF bit that has
+ // the same sig_d, and fills the output FfData according to the FF
+ // bits found. This function will only consider FF bits that are
+ // the only user of the given sig bits — if any bit in sig is used
+ // by anything other than a single FF, this function will return false.
+ //
+ // The returned FfData structure does not correspond to any actual FF
+ // cell in the design — it is the amalgamation of extracted FF bits,
+ // possibly coming from several FF cells.
+ //
+ // If some of the bits in sig have no users at all, this function
+ // will accept them as well (and fill returned FfData with dummy values
+ // for the given bit, effectively synthesizing an unused FF bit of the
+ // appropriate type). However, if all bits in sig are completely
+ // unused, this function will fail and return false (having no idea
+ // what kind of FF to produce) — use the above helper if that case
+ // is important to handle.
+ //
+ // Note that this function does not remove the FF bits returned from
+ // the design — this is so that the caller can decide whether to accept
+ // this FF for merging or not. If the result is accepted,
+ // remove_output_ff should be called on the second output argument.
+ bool find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair<Cell *, int>> &bits);
+
+ // Like above, but returns a FF to merge into a given cell input. Takes
+ // sig_q, which is the current cell input — it will search for FFs with
+ // matching sig_q.
+ //
+ // As opposed to find_output_ff, this function doesn't care about usage
+ // counts, and may return FF bits that also have other fanout. This
+ // should not be a problem for input FF merging.
+ //
+ // As a special case, if some of the bits in sig_q are constant, this
+ // function will accept them as well, by synthesizing in-place
+ // a constant-input FF bit (with matching initial value and reset value).
+ // However, this will not work if the input is all-constant — if the caller
+ // cares about this case, it needs to check for it explicitely.
+ bool find_input_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair<Cell *, int>> &bits);
+
+ // To be called on find_output_ff result that will be merged. This
+ // marks the given FF bits as used up (and not to be considered for
+ // further merging as inputs), and reconnects their Q ports to a dummy
+ // wire (since the wire previously connected there will now be driven
+ // by the merged-to cell instead).
+ void remove_output_ff(const pool<std::pair<Cell *, int>> &bits);
+
+ // To be called on find_input_ff result that will be merged. This
+ // marks the given FF bits as used, and disallows merging them as
+ // outputs. They can, however, still be merged as inputs again
+ // (perhaps for another cell).
+ void mark_input_ff(const pool<std::pair<Cell *, int>> &bits);
+
+ void set(FfInitVals *initvals_, RTLIL::Module *module_);
+
+ void clear();
+
+ FfMergeHelper(FfInitVals *initvals, RTLIL::Module *module) {
+ set(initvals, module);
+ }
+
+ FfMergeHelper() {}
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc
new file mode 100644
index 000000000..330c4d189
--- /dev/null
+++ b/kernel/fstdata.cc
@@ -0,0 +1,252 @@
+/*
+ * 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
+
+
+FstData::FstData(std::string filename) : ctx(nullptr)
+{
+ 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'\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);
+}
+
+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));
+ 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.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);
+ //log("adding %s.%s\n",var.scope.c_str(), clean_name.c_str());
+
+ name_to_handle[var.scope+"."+clean_name] = h->u.var.handle;
+ break;
+ }
+ }
+ }
+}
+
+static void reconstruct_edges_varlen(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_edges_callback(pnt_time, pnt_facidx, pnt_value, plen);
+}
+
+static void reconstruct_edges(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_edges_callback(pnt_time, pnt_facidx, pnt_value, plen);
+}
+
+void FstData::reconstruct_edges_callback(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t /* plen */)
+{
+ std::string val = std::string((const char *)pnt_value);
+ std::string prev = last_data[pnt_facidx];
+ if (pnt_time>=start_time) {
+ if (prev!="1" && val=="1")
+ edges.push_back(pnt_time);
+ if (prev!="0" && val=="0")
+ edges.push_back(pnt_time);
+ }
+ last_data[pnt_facidx] = val;
+}
+
+std::vector<uint64_t> FstData::getAllEdges(std::vector<fstHandle> &signal, uint64_t start, uint64_t end)
+{
+ start_time = start;
+ end_time = end;
+ last_data.clear();
+ for(auto &s : signal) {
+ last_data[s] = "x";
+ }
+ edges.clear();
+ fstReaderSetLimitTimeRange(ctx, start_time, end_time);
+ fstReaderClrFacProcessMaskAll(ctx);
+ for(const auto sig : signal)
+ fstReaderSetFacProcessMask(ctx,sig);
+ fstReaderIterBlocks2(ctx, reconstruct_edges, reconstruct_edges_varlen, this, nullptr);
+ return edges;
+}
+
+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 (sample_times_ndx >= sample_times.size()) return;
+
+ uint64_t time = sample_times[sample_times_ndx];
+ // if we are past the timestamp
+ if (pnt_time > time) {
+ for (auto const& c : last_data)
+ {
+ handle_to_data[c.first].push_back(std::make_pair(time,c.second));
+ size_t index = handle_to_data[c.first].size() - 1;
+ time_to_index[c.first][time] = index;
+ }
+ sample_times_ndx++;
+ }
+ // always update last_data
+ last_data[pnt_facidx] = std::string((const char *)pnt_value);
+}
+
+void FstData::reconstructAtTimes(std::vector<fstHandle> &signal, std::vector<uint64_t> time)
+{
+ handle_to_data.clear();
+ time_to_index.clear();
+ last_data.clear();
+ sample_times_ndx = 0;
+ sample_times = time;
+ fstReaderSetUnlimitedTimeRange(ctx);
+ fstReaderClrFacProcessMaskAll(ctx);
+ for(const auto sig : signal)
+ fstReaderSetFacProcessMask(ctx,sig);
+ fstReaderIterBlocks2(ctx, reconstruct_clb_attimes, reconstruct_clb_varlen_attimes, this, nullptr);
+
+ if (time_to_index[signal.back()].count(time.back())==0) {
+ for (auto const& c : last_data)
+ {
+ handle_to_data[c.first].push_back(std::make_pair(time.back(),c.second));
+ size_t index = handle_to_data[c.first].size() - 1;
+ time_to_index[c.first][time.back()] = index;
+ }
+ }
+}
+
+void FstData::reconstructAllAtTimes(std::vector<uint64_t> time)
+{
+ handle_to_data.clear();
+ time_to_index.clear();
+ last_data.clear();
+ sample_times_ndx = 0;
+ sample_times = time;
+
+ fstReaderSetUnlimitedTimeRange(ctx);
+ fstReaderSetFacProcessMaskAll(ctx);
+ fstReaderIterBlocks2(ctx, reconstruct_clb_attimes, reconstruct_clb_varlen_attimes, this, nullptr);
+
+ if (time_to_index[1].count(time.back())==0) {
+ for (auto const& c : last_data)
+ {
+ handle_to_data[c.first].push_back(std::make_pair(time.back(),c.second));
+ size_t index = handle_to_data[c.first].size() - 1;
+ time_to_index[c.first][time.back()] = index;
+ }
+ }
+}
+
+std::string FstData::valueAt(fstHandle signal, uint64_t time)
+{
+ if (handle_to_data.find(signal) == handle_to_data.end())
+ log_error("Signal id %d not found\n", (int)signal);
+ auto &data = handle_to_data[signal];
+ if (time_to_index[signal].count(time)!=0) {
+ size_t index = time_to_index[signal][time];
+ return data.at(index).second;
+ } else {
+ log_error("No data for signal %d at time %d\n", (int)signal, (int)time);
+ }
+}
diff --git a/kernel/fstdata.h b/kernel/fstdata.h
new file mode 100644
index 000000000..c069ff5e5
--- /dev/null
+++ b/kernel/fstdata.h
@@ -0,0 +1,81 @@
+/*
+ * 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
+
+struct FstVar
+{
+ fstHandle id;
+ std::string name;
+ bool is_alias;
+ 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_edges_callback(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen);
+ std::vector<uint64_t> getAllEdges(std::vector<fstHandle> &signal, uint64_t start_time, uint64_t end_time);
+
+ void reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen);
+ void reconstructAtTimes(std::vector<fstHandle> &signal,std::vector<uint64_t> time);
+ void reconstructAllAtTimes(std::vector<uint64_t> time);
+
+ std::string valueAt(fstHandle signal, uint64_t time);
+ 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::vector<std::pair<uint64_t, std::string>>> handle_to_data;
+ std::map<fstHandle, std::string> last_data;
+ std::map<fstHandle, std::map<uint64_t, size_t>> time_to_index;
+ std::vector<uint64_t> sample_times;
+ size_t sample_times_ndx;
+ double timescale;
+ std::string timescale_str;
+ uint64_t start_time;
+ uint64_t end_time;
+ std::vector<uint64_t> edges;
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/hashlib.h b/kernel/hashlib.h
index a523afadd..0c9f25287 100644
--- a/kernel/hashlib.h
+++ b/kernel/hashlib.h
@@ -6,7 +6,7 @@
// means.
// -------------------------------------------------------
-// Written by Clifford Wolf <clifford@clifford.at> in 2014
+// Written by Claire Xenia Wolf <claire@yosyshq.com> in 2014
// -------------------------------------------------------
#ifndef HASHLIB_H
@@ -66,6 +66,12 @@ struct hash_int_ops {
}
};
+template<> struct hash_ops<bool> : hash_int_ops
+{
+ static inline unsigned int hash(bool a) {
+ return a ? 1 : 0;
+ }
+};
template<> struct hash_ops<int32_t> : hash_int_ops
{
static inline unsigned int hash(int32_t a) {
diff --git a/kernel/log.cc b/kernel/log.cc
index 41e91119e..4bcce3b28 100644
--- a/kernel/log.cc
+++ b/kernel/log.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -71,8 +71,6 @@ int string_buf_index = -1;
static struct timeval initial_tv = { 0, 0 };
static bool next_print_log = false;
static int log_newline_count = 0;
-static bool check_expected_logs = true;
-static bool display_error_log_msg = true;
static void log_id_cache_clear()
{
@@ -339,8 +337,7 @@ static void logv_error_with_prefix(const char *prefix,
f = stderr;
log_last_error = vstringf(format, ap);
- if (display_error_log_msg)
- log("%s%s", prefix, log_last_error.c_str());
+ log("%s%s", prefix, log_last_error.c_str());
log_flush();
log_make_debug = bak_log_make_debug;
@@ -349,8 +346,7 @@ static void logv_error_with_prefix(const char *prefix,
if (YS_REGEX_NS::regex_search(log_last_error, item.second.pattern))
item.second.current_count++;
- if (check_expected_logs)
- log_check_expected();
+ log_check_expected();
if (log_error_atexit)
log_error_atexit();
@@ -667,9 +663,14 @@ void log_wire(RTLIL::Wire *wire, std::string indent)
void log_check_expected()
{
- check_expected_logs = false;
+ // copy out all of the expected logs so that they cannot be re-checked
+ // or match against themselves
+ dict<std::string, LogExpectedItem> expect_log, expect_warning, expect_error;
+ std::swap(expect_warning, log_expect_warning);
+ std::swap(expect_log, log_expect_log);
+ std::swap(expect_error, log_expect_error);
- for (auto &item : log_expect_warning) {
+ for (auto &item : expect_warning) {
if (item.second.current_count == 0) {
log_warn_regexes.clear();
log_error("Expected warning pattern '%s' not found !\n", item.first.c_str());
@@ -681,7 +682,7 @@ void log_check_expected()
}
}
- for (auto &item : log_expect_log) {
+ for (auto &item : expect_log) {
if (item.second.current_count == 0) {
log_warn_regexes.clear();
log_error("Expected log pattern '%s' not found !\n", item.first.c_str());
@@ -693,7 +694,7 @@ void log_check_expected()
}
}
- for (auto &item : log_expect_error)
+ for (auto &item : expect_error)
if (item.second.current_count == item.second.expected_count) {
log_warn_regexes.clear();
log("Expected error pattern '%s' found !!!\n", item.first.c_str());
@@ -705,7 +706,6 @@ void log_check_expected()
_Exit(0);
#endif
} else {
- display_error_log_msg = false;
log_warn_regexes.clear();
log_error("Expected error pattern '%s' not found !\n", item.first.c_str());
}
diff --git a/kernel/log.h b/kernel/log.h
index 8981c4cde..ea14028dd 100644
--- a/kernel/log.h
+++ b/kernel/log.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -24,9 +24,29 @@
#include <time.h>
-// In GCC 4.8 std::regex is not working correctlty, in order to make features
-// using regular expressions to work replacement regex library is used
-#if defined(__GNUC__) && !defined( __clang__) && ( __GNUC__ == 4 && __GNUC_MINOR__ <= 8)
+// In the libstdc++ headers that are provided by GCC 4.8, std::regex is not
+// working correctly. In order to make features using regular expressions
+// work, a replacement regex library is used. Just checking for GCC version
+// is not enough though, because at least on RHEL7/CentOS7 even when compiling
+// with Clang instead of GCC, the GCC 4.8 headers are still used for std::regex.
+// We have to check the version of the libstdc++ headers specifically, not the
+// compiler version. GCC headers of libstdc++ before version 3.4 define
+// __GLIBCPP__, later versions define __GLIBCXX__. GCC 7 and newer additionaly
+// define _GLIBCXX_RELEASE with a version number.
+// Include limits std C++ header, so we get the version macros defined:
+#if defined(__cplusplus)
+# include <limits>
+#endif
+// Check if libstdc++ is from GCC
+#if defined(__GLIBCPP__) || defined(__GLIBCXX__)
+// Check if version could be 4.8 or lower (this also matches for some 4.9 and
+// 5.0 releases). See:
+// https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html#abi.versioning
+# if !defined(_GLIBCXX_RELEASE) && (defined(__GLIBCPP__) || __GLIBCXX__ <= 20150623)
+# define YS_HAS_BAD_STD_REGEX
+# endif
+#endif
+#if defined(YS_HAS_BAD_STD_REGEX)
#include <boost/xpressive/xpressive.hpp>
#define YS_REGEX_TYPE boost::xpressive::sregex
#define YS_REGEX_MATCH_TYPE boost::xpressive::smatch
diff --git a/kernel/macc.h b/kernel/macc.h
index d216e6772..e4e1ebf52 100644
--- a/kernel/macc.h
+++ b/kernel/macc.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/kernel/mem.cc b/kernel/mem.cc
index 0301a913c..059f8f934 100644
--- a/kernel/mem.cc
+++ b/kernel/mem.cc
@@ -18,6 +18,7 @@
*/
#include "kernel/mem.h"
+#include "kernel/ff.h"
USING_YOSYS_NAMESPACE
@@ -52,6 +53,67 @@ void Mem::remove() {
}
void Mem::emit() {
+ check();
+ std::vector<int> rd_left;
+ for (int i = 0; i < GetSize(rd_ports); i++) {
+ auto &port = rd_ports[i];
+ if (port.removed) {
+ if (port.cell) {
+ module->remove(port.cell);
+ }
+ } else {
+ rd_left.push_back(i);
+ }
+ }
+ std::vector<int> wr_left;
+ for (int i = 0; i < GetSize(wr_ports); i++) {
+ auto &port = wr_ports[i];
+ if (port.removed) {
+ if (port.cell) {
+ module->remove(port.cell);
+ }
+ } else {
+ wr_left.push_back(i);
+ }
+ }
+ std::vector<int> init_left;
+ for (int i = 0; i < GetSize(inits); i++) {
+ auto &init = inits[i];
+ if (init.removed) {
+ if (init.cell) {
+ module->remove(init.cell);
+ }
+ } else {
+ init_left.push_back(i);
+ }
+ }
+ for (int i = 0; i < GetSize(rd_left); i++)
+ if (i != rd_left[i])
+ std::swap(rd_ports[i], rd_ports[rd_left[i]]);
+ rd_ports.resize(GetSize(rd_left));
+ for (int i = 0; i < GetSize(wr_left); i++)
+ if (i != wr_left[i])
+ std::swap(wr_ports[i], wr_ports[wr_left[i]]);
+ wr_ports.resize(GetSize(wr_left));
+ for (int i = 0; i < GetSize(init_left); i++)
+ if (i != init_left[i])
+ std::swap(inits[i], inits[init_left[i]]);
+ inits.resize(GetSize(init_left));
+
+ for (auto &port : rd_ports) {
+ for (int i = 0; i < GetSize(wr_left); i++) {
+ port.transparency_mask[i] = port.transparency_mask[wr_left[i]];
+ port.collision_x_mask[i] = port.collision_x_mask[wr_left[i]];
+ }
+ port.transparency_mask.resize(GetSize(wr_left));
+ port.collision_x_mask.resize(GetSize(wr_left));
+ }
+ for (auto &port : wr_ports) {
+ for (int i = 0; i < GetSize(wr_left); i++)
+ port.priority_mask[i] = port.priority_mask[wr_left[i]];
+ port.priority_mask.resize(GetSize(wr_left));
+ }
+
if (packed) {
if (mem) {
module->memories.erase(mem->name);
@@ -61,54 +123,90 @@ void Mem::emit() {
if (!cell) {
if (memid.empty())
memid = NEW_ID;
- cell = module->addCell(memid, ID($mem));
+ cell = module->addCell(memid, ID($mem_v2));
}
+ cell->type = ID($mem_v2);
cell->attributes = attributes;
cell->parameters[ID::MEMID] = Const(memid.str());
cell->parameters[ID::WIDTH] = Const(width);
cell->parameters[ID::OFFSET] = Const(start_offset);
cell->parameters[ID::SIZE] = Const(size);
- cell->parameters[ID::RD_PORTS] = Const(GetSize(rd_ports));
- cell->parameters[ID::WR_PORTS] = Const(GetSize(wr_ports));
- Const rd_clk_enable, rd_clk_polarity, rd_transparent;
- Const wr_clk_enable, wr_clk_polarity;
+ Const rd_wide_continuation, rd_clk_enable, rd_clk_polarity, rd_transparency_mask, rd_collision_x_mask;
+ Const wr_wide_continuation, wr_clk_enable, wr_clk_polarity, wr_priority_mask;
+ Const rd_ce_over_srst, rd_arst_value, rd_srst_value, rd_init_value;
SigSpec rd_clk, rd_en, rd_addr, rd_data;
SigSpec wr_clk, wr_en, wr_addr, wr_data;
+ SigSpec rd_arst, rd_srst;
int abits = 0;
for (auto &port : rd_ports)
abits = std::max(abits, GetSize(port.addr));
for (auto &port : wr_ports)
abits = std::max(abits, GetSize(port.addr));
cell->parameters[ID::ABITS] = Const(abits);
+ std::vector<int> wr_port_xlat;
+ for (int i = 0; i < GetSize(wr_ports); i++)
+ for (int j = 0; j < (1 << wr_ports[i].wide_log2); j++)
+ wr_port_xlat.push_back(i);
for (auto &port : rd_ports) {
if (port.cell) {
module->remove(port.cell);
port.cell = nullptr;
}
- rd_clk_enable.bits.push_back(State(port.clk_enable));
- rd_clk_polarity.bits.push_back(State(port.clk_polarity));
- rd_transparent.bits.push_back(State(port.transparent));
- rd_clk.append(port.clk);
- log_assert(GetSize(port.clk) == 1);
- rd_en.append(port.en);
- log_assert(GetSize(port.en) == 1);
- SigSpec addr = port.addr;
- addr.extend_u0(abits, false);
- rd_addr.append(addr);
- log_assert(GetSize(addr) == abits);
+ for (int sub = 0; sub < (1 << port.wide_log2); sub++)
+ {
+ rd_wide_continuation.bits.push_back(State(sub != 0));
+ rd_clk_enable.bits.push_back(State(port.clk_enable));
+ rd_clk_polarity.bits.push_back(State(port.clk_polarity));
+ rd_ce_over_srst.bits.push_back(State(port.ce_over_srst));
+ rd_clk.append(port.clk);
+ rd_arst.append(port.arst);
+ rd_srst.append(port.srst);
+ rd_en.append(port.en);
+ SigSpec addr = port.sub_addr(sub);
+ addr.extend_u0(abits, false);
+ rd_addr.append(addr);
+ log_assert(GetSize(addr) == abits);
+ for (auto idx : wr_port_xlat) {
+ rd_transparency_mask.bits.push_back(State(bool(port.transparency_mask[idx])));
+ rd_collision_x_mask.bits.push_back(State(bool(port.collision_x_mask[idx])));
+ }
+ }
rd_data.append(port.data);
- log_assert(GetSize(port.data) == width);
+ for (auto &bit : port.arst_value)
+ rd_arst_value.bits.push_back(bit);
+ for (auto &bit : port.srst_value)
+ rd_srst_value.bits.push_back(bit);
+ for (auto &bit : port.init_value)
+ rd_init_value.bits.push_back(bit);
}
if (rd_ports.empty()) {
+ rd_wide_continuation = State::S0;
rd_clk_enable = State::S0;
rd_clk_polarity = State::S0;
- rd_transparent = State::S0;
+ rd_ce_over_srst = State::S0;
+ rd_arst_value = State::S0;
+ rd_srst_value = State::S0;
+ rd_init_value = State::S0;
+ }
+ if (rd_ports.empty() || wr_ports.empty()) {
+ rd_transparency_mask = State::S0;
+ rd_collision_x_mask = State::S0;
}
+ cell->parameters[ID::RD_PORTS] = Const(GetSize(rd_clk));
cell->parameters[ID::RD_CLK_ENABLE] = rd_clk_enable;
cell->parameters[ID::RD_CLK_POLARITY] = rd_clk_polarity;
- cell->parameters[ID::RD_TRANSPARENT] = rd_transparent;
+ cell->parameters[ID::RD_TRANSPARENCY_MASK] = rd_transparency_mask;
+ cell->parameters[ID::RD_COLLISION_X_MASK] = rd_collision_x_mask;
+ cell->parameters[ID::RD_WIDE_CONTINUATION] = rd_wide_continuation;
+ cell->parameters[ID::RD_CE_OVER_SRST] = rd_ce_over_srst;
+ cell->parameters[ID::RD_ARST_VALUE] = rd_arst_value;
+ cell->parameters[ID::RD_SRST_VALUE] = rd_srst_value;
+ cell->parameters[ID::RD_INIT_VALUE] = rd_init_value;
+ cell->parameters.erase(ID::RD_TRANSPARENT);
cell->setPort(ID::RD_CLK, rd_clk);
cell->setPort(ID::RD_EN, rd_en);
+ cell->setPort(ID::RD_ARST, rd_arst);
+ cell->setPort(ID::RD_SRST, rd_srst);
cell->setPort(ID::RD_ADDR, rd_addr);
cell->setPort(ID::RD_DATA, rd_data);
for (auto &port : wr_ports) {
@@ -116,25 +214,33 @@ void Mem::emit() {
module->remove(port.cell);
port.cell = nullptr;
}
- wr_clk_enable.bits.push_back(State(port.clk_enable));
- wr_clk_polarity.bits.push_back(State(port.clk_polarity));
- wr_clk.append(port.clk);
- log_assert(GetSize(port.clk) == 1);
+ for (int sub = 0; sub < (1 << port.wide_log2); sub++)
+ {
+ wr_wide_continuation.bits.push_back(State(sub != 0));
+ wr_clk_enable.bits.push_back(State(port.clk_enable));
+ wr_clk_polarity.bits.push_back(State(port.clk_polarity));
+ wr_clk.append(port.clk);
+ for (auto idx : wr_port_xlat)
+ wr_priority_mask.bits.push_back(State(bool(port.priority_mask[idx])));
+ SigSpec addr = port.sub_addr(sub);
+ addr.extend_u0(abits, false);
+ wr_addr.append(addr);
+ log_assert(GetSize(addr) == abits);
+ }
wr_en.append(port.en);
- log_assert(GetSize(port.en) == width);
- SigSpec addr = port.addr;
- addr.extend_u0(abits, false);
- wr_addr.append(addr);
- log_assert(GetSize(addr) == abits);
wr_data.append(port.data);
- log_assert(GetSize(port.data) == width);
}
if (wr_ports.empty()) {
+ wr_wide_continuation = State::S0;
wr_clk_enable = State::S0;
wr_clk_polarity = State::S0;
+ wr_priority_mask = State::S0;
}
+ cell->parameters[ID::WR_PORTS] = Const(GetSize(wr_clk));
cell->parameters[ID::WR_CLK_ENABLE] = wr_clk_enable;
cell->parameters[ID::WR_CLK_POLARITY] = wr_clk_polarity;
+ cell->parameters[ID::WR_PRIORITY_MASK] = wr_priority_mask;
+ cell->parameters[ID::WR_WIDE_CONTINUATION] = wr_wide_continuation;
cell->setPort(ID::WR_CLK, wr_clk);
cell->setPort(ID::WR_EN, wr_en);
cell->setPort(ID::WR_ADDR, wr_addr);
@@ -161,30 +267,46 @@ void Mem::emit() {
mem->width = width;
mem->start_offset = start_offset;
mem->size = size;
+ mem->attributes = attributes;
for (auto &port : rd_ports) {
if (!port.cell)
- port.cell = module->addCell(NEW_ID, ID($memrd));
+ port.cell = module->addCell(NEW_ID, ID($memrd_v2));
+ port.cell->type = ID($memrd_v2);
+ port.cell->attributes = port.attributes;
port.cell->parameters[ID::MEMID] = memid.str();
port.cell->parameters[ID::ABITS] = GetSize(port.addr);
- port.cell->parameters[ID::WIDTH] = width;
+ port.cell->parameters[ID::WIDTH] = width << port.wide_log2;
port.cell->parameters[ID::CLK_ENABLE] = port.clk_enable;
port.cell->parameters[ID::CLK_POLARITY] = port.clk_polarity;
- port.cell->parameters[ID::TRANSPARENT] = port.transparent;
+ port.cell->parameters[ID::CE_OVER_SRST] = port.ce_over_srst;
+ port.cell->parameters[ID::ARST_VALUE] = port.arst_value;
+ port.cell->parameters[ID::SRST_VALUE] = port.srst_value;
+ port.cell->parameters[ID::INIT_VALUE] = port.init_value;
+ port.cell->parameters[ID::TRANSPARENCY_MASK] = port.transparency_mask;
+ port.cell->parameters[ID::COLLISION_X_MASK] = port.collision_x_mask;
+ port.cell->parameters.erase(ID::TRANSPARENT);
port.cell->setPort(ID::CLK, port.clk);
port.cell->setPort(ID::EN, port.en);
+ port.cell->setPort(ID::ARST, port.arst);
+ port.cell->setPort(ID::SRST, port.srst);
port.cell->setPort(ID::ADDR, port.addr);
port.cell->setPort(ID::DATA, port.data);
}
int idx = 0;
for (auto &port : wr_ports) {
if (!port.cell)
- port.cell = module->addCell(NEW_ID, ID($memwr));
+ port.cell = module->addCell(NEW_ID, ID($memwr_v2));
+ port.cell->type = ID($memwr_v2);
+ port.cell->attributes = port.attributes;
+ if (port.cell->parameters.count(ID::PRIORITY))
+ port.cell->parameters.erase(ID::PRIORITY);
port.cell->parameters[ID::MEMID] = memid.str();
port.cell->parameters[ID::ABITS] = GetSize(port.addr);
- port.cell->parameters[ID::WIDTH] = width;
+ port.cell->parameters[ID::WIDTH] = width << port.wide_log2;
port.cell->parameters[ID::CLK_ENABLE] = port.clk_enable;
port.cell->parameters[ID::CLK_POLARITY] = port.clk_polarity;
- port.cell->parameters[ID::PRIORITY] = idx++;
+ port.cell->parameters[ID::PORTID] = idx++;
+ port.cell->parameters[ID::PRIORITY_MASK] = port.priority_mask;
port.cell->setPort(ID::CLK, port.clk);
port.cell->setPort(ID::EN, port.en);
port.cell->setPort(ID::ADDR, port.addr);
@@ -192,8 +314,12 @@ void Mem::emit() {
}
idx = 0;
for (auto &init : inits) {
+ bool v2 = !init.en.is_fully_ones();
if (!init.cell)
- init.cell = module->addCell(NEW_ID, ID($meminit));
+ init.cell = module->addCell(NEW_ID, v2 ? ID($meminit_v2) : ID($meminit));
+ else
+ init.cell->type = v2 ? ID($meminit_v2) : ID($meminit);
+ init.cell->attributes = init.attributes;
init.cell->parameters[ID::MEMID] = memid.str();
init.cell->parameters[ID::ABITS] = GetSize(init.addr);
init.cell->parameters[ID::WIDTH] = width;
@@ -201,42 +327,185 @@ void Mem::emit() {
init.cell->parameters[ID::PRIORITY] = idx++;
init.cell->setPort(ID::ADDR, init.addr);
init.cell->setPort(ID::DATA, init.data);
+ if (v2)
+ init.cell->setPort(ID::EN, init.en);
+ else
+ init.cell->unsetPort(ID::EN);
}
}
}
-void Mem::remove_wr_port(int idx) {
- if (wr_ports[idx].cell) {
- module->remove(wr_ports[idx].cell);
- }
- wr_ports.erase(wr_ports.begin() + idx);
+void Mem::clear_inits() {
+ for (auto &init : inits)
+ init.removed = true;
}
-void Mem::remove_rd_port(int idx) {
- if (rd_ports[idx].cell) {
- module->remove(rd_ports[idx].cell);
+void Mem::coalesce_inits() {
+ // start address -> end address
+ std::map<int, int> chunks;
+ // Figure out chunk boundaries.
+ for (auto &init : inits) {
+ if (init.removed)
+ continue;
+ bool valid = false;
+ for (auto bit : init.en)
+ if (bit == State::S1)
+ valid = true;
+ if (!valid) {
+ init.removed = true;
+ continue;
+ }
+ int addr = init.addr.as_int();
+ int addr_e = addr + GetSize(init.data) / width;
+ auto it_e = chunks.upper_bound(addr_e);
+ auto it = it_e;
+ while (it != chunks.begin()) {
+ --it;
+ if (it->second < addr) {
+ ++it;
+ break;
+ }
+ }
+ if (it == it_e) {
+ // No overlapping inits — add this one to index.
+ chunks[addr] = addr_e;
+ } else {
+ // We have an overlap — all chunks in the [it, it_e)
+ // range will be merged with this init.
+ if (it->first < addr)
+ addr = it->first;
+ auto it_last = it_e;
+ it_last--;
+ if (it_last->second > addr_e)
+ addr_e = it_last->second;
+ chunks.erase(it, it_e);
+ chunks[addr] = addr_e;
+ }
+ }
+ // Group inits by the chunk they belong to.
+ dict<int, std::vector<int>> inits_by_chunk;
+ for (int i = 0; i < GetSize(inits); i++) {
+ auto &init = inits[i];
+ if (init.removed)
+ continue;
+ auto it = chunks.upper_bound(init.addr.as_int());
+ --it;
+ inits_by_chunk[it->first].push_back(i);
+ int addr = init.addr.as_int();
+ int addr_e = addr + GetSize(init.data) / width;
+ log_assert(addr >= it->first && addr_e <= it->second);
+ }
+ // Process each chunk.
+ for (auto &it : inits_by_chunk) {
+ int caddr = it.first;
+ int caddr_e = chunks[caddr];
+ auto &chunk_inits = it.second;
+ if (GetSize(chunk_inits) == 1) {
+ auto &init = inits[chunk_inits[0]];
+ if (!init.en.is_fully_ones()) {
+ for (int i = 0; i < GetSize(init.data); i++)
+ if (init.en[i % width] != State::S1)
+ init.data[i] = State::Sx;
+ init.en = Const(State::S1, width);
+ }
+ continue;
+ }
+ Const cdata(State::Sx, (caddr_e - caddr) * width);
+ for (int idx : chunk_inits) {
+ auto &init = inits[idx];
+ int offset = (init.addr.as_int() - caddr) * width;
+ log_assert(offset >= 0);
+ log_assert(offset + GetSize(init.data) <= GetSize(cdata));
+ for (int i = 0; i < GetSize(init.data); i++)
+ if (init.en[i % width] == State::S1)
+ cdata.bits[i+offset] = init.data.bits[i];
+ init.removed = true;
+ }
+ MemInit new_init;
+ new_init.addr = caddr;
+ new_init.data = cdata;
+ new_init.en = Const(State::S1, width);
+ inits.push_back(new_init);
}
- rd_ports.erase(rd_ports.begin() + idx);
-}
-
-void Mem::clear_inits() {
- for (auto &init : inits)
- if (init.cell)
- module->remove(init.cell);
- inits.clear();
}
Const Mem::get_init_data() const {
Const init_data(State::Sx, width * size);
for (auto &init : inits) {
+ if (init.removed)
+ continue;
int offset = (init.addr.as_int() - start_offset) * width;
for (int i = 0; i < GetSize(init.data); i++)
- if (0 <= i+offset && i+offset < GetSize(init_data))
+ if (0 <= i+offset && i+offset < GetSize(init_data) && init.en[i % width] == State::S1)
init_data.bits[i+offset] = init.data.bits[i];
}
return init_data;
}
+void Mem::check() {
+ int max_wide_log2 = 0;
+ for (auto &port : rd_ports) {
+ if (port.removed)
+ continue;
+ log_assert(GetSize(port.clk) == 1);
+ log_assert(GetSize(port.en) == 1);
+ log_assert(GetSize(port.arst) == 1);
+ log_assert(GetSize(port.srst) == 1);
+ log_assert(GetSize(port.data) == (width << port.wide_log2));
+ log_assert(GetSize(port.init_value) == (width << port.wide_log2));
+ log_assert(GetSize(port.arst_value) == (width << port.wide_log2));
+ log_assert(GetSize(port.srst_value) == (width << port.wide_log2));
+ if (!port.clk_enable) {
+ log_assert(port.en == State::S1);
+ log_assert(port.arst == State::S0);
+ log_assert(port.srst == State::S0);
+ }
+ for (int j = 0; j < port.wide_log2; j++) {
+ log_assert(port.addr[j] == State::S0);
+ }
+ max_wide_log2 = std::max(max_wide_log2, port.wide_log2);
+ log_assert(GetSize(port.transparency_mask) == GetSize(wr_ports));
+ log_assert(GetSize(port.collision_x_mask) == GetSize(wr_ports));
+ for (int j = 0; j < GetSize(wr_ports); j++) {
+ auto &wport = wr_ports[j];
+ if ((port.transparency_mask[j] || port.collision_x_mask[j]) && !wport.removed) {
+ log_assert(port.clk_enable);
+ log_assert(wport.clk_enable);
+ log_assert(port.clk == wport.clk);
+ log_assert(port.clk_polarity == wport.clk_polarity);
+ }
+ log_assert(!port.transparency_mask[j] || !port.collision_x_mask[j]);
+ }
+ }
+ for (int i = 0; i < GetSize(wr_ports); i++) {
+ auto &port = wr_ports[i];
+ if (port.removed)
+ continue;
+ log_assert(GetSize(port.clk) == 1);
+ log_assert(GetSize(port.en) == (width << port.wide_log2));
+ log_assert(GetSize(port.data) == (width << port.wide_log2));
+ for (int j = 0; j < port.wide_log2; j++) {
+ log_assert(port.addr[j] == State::S0);
+ }
+ max_wide_log2 = std::max(max_wide_log2, port.wide_log2);
+ log_assert(GetSize(port.priority_mask) == GetSize(wr_ports));
+ for (int j = 0; j < GetSize(wr_ports); j++) {
+ auto &wport = wr_ports[j];
+ if (port.priority_mask[j] && !wport.removed) {
+ log_assert(j < i);
+ log_assert(port.clk_enable == wport.clk_enable);
+ if (port.clk_enable) {
+ log_assert(port.clk == wport.clk);
+ log_assert(port.clk_polarity == wport.clk_polarity);
+ }
+ }
+ }
+ }
+ int mask = (1 << max_wide_log2) - 1;
+ log_assert(!(start_offset & mask));
+ log_assert(!(size & mask));
+}
+
namespace {
struct MemIndex {
@@ -245,11 +514,11 @@ namespace {
dict<IdString, pool<Cell *>> inits;
MemIndex (Module *module) {
for (auto cell: module->cells()) {
- if (cell->type == ID($memwr))
+ if (cell->type.in(ID($memwr), ID($memwr_v2)))
wr_ports[cell->parameters.at(ID::MEMID).decode_string()].insert(cell);
- else if (cell->type == ID($memrd))
+ else if (cell->type.in(ID($memrd), ID($memrd_v2)))
rd_ports[cell->parameters.at(ID::MEMID).decode_string()].insert(cell);
- else if (cell->type == ID($meminit))
+ else if (cell->type.in(ID($meminit), ID($meminit_v2)))
inits[cell->parameters.at(ID::MEMID).decode_string()].insert(cell);
}
}
@@ -260,25 +529,56 @@ namespace {
res.packed = false;
res.mem = mem;
res.attributes = mem->attributes;
+ std::vector<bool> rd_transparent;
+ std::vector<int> wr_portid;
if (index.rd_ports.count(mem->name)) {
for (auto cell : index.rd_ports.at(mem->name)) {
MemRd mrd;
+ bool is_compat = cell->type == ID($memrd);
mrd.cell = cell;
mrd.attributes = cell->attributes;
mrd.clk_enable = cell->parameters.at(ID::CLK_ENABLE).as_bool();
mrd.clk_polarity = cell->parameters.at(ID::CLK_POLARITY).as_bool();
- mrd.transparent = cell->parameters.at(ID::TRANSPARENT).as_bool();
mrd.clk = cell->getPort(ID::CLK);
mrd.en = cell->getPort(ID::EN);
mrd.addr = cell->getPort(ID::ADDR);
mrd.data = cell->getPort(ID::DATA);
+ mrd.wide_log2 = ceil_log2(GetSize(mrd.data) / mem->width);
+ bool transparent = false;
+ if (is_compat) {
+ transparent = cell->parameters.at(ID::TRANSPARENT).as_bool();
+ mrd.ce_over_srst = false;
+ mrd.arst_value = Const(State::Sx, mem->width << mrd.wide_log2);
+ mrd.srst_value = Const(State::Sx, mem->width << mrd.wide_log2);
+ mrd.init_value = Const(State::Sx, mem->width << mrd.wide_log2);
+ mrd.srst = State::S0;
+ mrd.arst = State::S0;
+ if (!mrd.clk_enable) {
+ // Fix some patterns that we'll allow for backwards compatibility,
+ // but don't want to see moving forwards: async transparent
+ // ports (inherently meaningless) and async ports without
+ // const 1 tied to EN bit (which may mean a latch in the future).
+ transparent = false;
+ if (mrd.en == State::Sx)
+ mrd.en = State::S1;
+ }
+ } else {
+ mrd.ce_over_srst = cell->parameters.at(ID::CE_OVER_SRST).as_bool();
+ mrd.arst_value = cell->parameters.at(ID::ARST_VALUE);
+ mrd.srst_value = cell->parameters.at(ID::SRST_VALUE);
+ mrd.init_value = cell->parameters.at(ID::INIT_VALUE);
+ mrd.arst = cell->getPort(ID::ARST);
+ mrd.srst = cell->getPort(ID::SRST);
+ }
res.rd_ports.push_back(mrd);
+ rd_transparent.push_back(transparent);
}
}
if (index.wr_ports.count(mem->name)) {
std::vector<std::pair<int, MemWr>> ports;
for (auto cell : index.wr_ports.at(mem->name)) {
MemWr mwr;
+ bool is_compat = cell->type == ID($memwr);
mwr.cell = cell;
mwr.attributes = cell->attributes;
mwr.clk_enable = cell->parameters.at(ID::CLK_ENABLE).as_bool();
@@ -287,11 +587,37 @@ namespace {
mwr.en = cell->getPort(ID::EN);
mwr.addr = cell->getPort(ID::ADDR);
mwr.data = cell->getPort(ID::DATA);
- ports.push_back(std::make_pair(cell->parameters.at(ID::PRIORITY).as_int(), mwr));
+ mwr.wide_log2 = ceil_log2(GetSize(mwr.data) / mem->width);
+ ports.push_back(std::make_pair(cell->parameters.at(is_compat ? ID::PRIORITY : ID::PORTID).as_int(), mwr));
}
std::sort(ports.begin(), ports.end(), [](const std::pair<int, MemWr> &a, const std::pair<int, MemWr> &b) { return a.first < b.first; });
- for (auto &it : ports)
+ for (auto &it : ports) {
res.wr_ports.push_back(it.second);
+ wr_portid.push_back(it.first);
+ }
+ for (int i = 0; i < GetSize(res.wr_ports); i++) {
+ auto &port = res.wr_ports[i];
+ bool is_compat = port.cell->type == ID($memwr);
+ if (is_compat) {
+ port.priority_mask.resize(GetSize(res.wr_ports));
+ for (int j = 0; j < i; j++) {
+ auto &oport = res.wr_ports[j];
+ if (port.clk_enable != oport.clk_enable)
+ continue;
+ if (port.clk_enable && port.clk != oport.clk)
+ continue;
+ if (port.clk_enable && port.clk_polarity != oport.clk_polarity)
+ continue;
+ port.priority_mask[j] = true;
+ }
+ } else {
+ Const orig_prio_mask = port.cell->parameters.at(ID::PRIORITY_MASK);
+ for (int orig_portid : wr_portid) {
+ bool has_prio = orig_portid < GetSize(orig_prio_mask) && orig_prio_mask[orig_portid] == State::S1;
+ port.priority_mask.push_back(has_prio);
+ }
+ }
+ }
}
if (index.inits.count(mem->name)) {
std::vector<std::pair<int, MemInit>> inits;
@@ -307,12 +633,50 @@ namespace {
log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell));
init.addr = addr.as_const();
init.data = data.as_const();
+ if (cell->type == ID($meminit_v2)) {
+ auto en = cell->getPort(ID::EN);
+ if (!en.is_fully_const())
+ log_error("Non-constant enable %s in memory initialization %s.\n", log_signal(en), log_id(cell));
+ init.en = en.as_const();
+ } else {
+ init.en = RTLIL::Const(State::S1, mem->width);
+ }
inits.push_back(std::make_pair(cell->parameters.at(ID::PRIORITY).as_int(), init));
}
std::sort(inits.begin(), inits.end(), [](const std::pair<int, MemInit> &a, const std::pair<int, MemInit> &b) { return a.first < b.first; });
for (auto &it : inits)
res.inits.push_back(it.second);
}
+ for (int i = 0; i < GetSize(res.rd_ports); i++) {
+ auto &port = res.rd_ports[i];
+ bool is_compat = port.cell->type == ID($memrd);
+ if (is_compat) {
+ port.transparency_mask.resize(GetSize(res.wr_ports));
+ port.collision_x_mask.resize(GetSize(res.wr_ports));
+ if (!rd_transparent[i])
+ continue;
+ if (!port.clk_enable)
+ continue;
+ for (int j = 0; j < GetSize(res.wr_ports); j++) {
+ auto &wport = res.wr_ports[j];
+ if (!wport.clk_enable)
+ continue;
+ if (port.clk != wport.clk)
+ continue;
+ if (port.clk_polarity != wport.clk_polarity)
+ continue;
+ port.transparency_mask[j] = true;
+ }
+ } else {
+ Const orig_trans_mask = port.cell->parameters.at(ID::TRANSPARENCY_MASK);
+ Const orig_cx_mask = port.cell->parameters.at(ID::COLLISION_X_MASK);
+ for (int orig_portid : wr_portid) {
+ port.transparency_mask.push_back(orig_portid < GetSize(orig_trans_mask) && orig_trans_mask[orig_portid] == State::S1);
+ port.collision_x_mask.push_back(orig_portid < GetSize(orig_cx_mask) && orig_cx_mask[orig_portid] == State::S1);
+ }
+ }
+ }
+ res.check();
return res;
}
@@ -322,6 +686,7 @@ namespace {
cell->parameters.at(ID::OFFSET).as_int(),
cell->parameters.at(ID::SIZE).as_int()
);
+ bool is_compat = cell->type == ID($mem);
int abits = cell->parameters.at(ID::ABITS).as_int();
res.packed = true;
res.cell = cell;
@@ -343,32 +708,112 @@ namespace {
MemInit minit;
minit.addr = res.start_offset + pos;
minit.data = init.extract(pos * res.width, (epos - pos) * res.width, State::Sx);
+ minit.en = RTLIL::Const(State::S1, res.width);
res.inits.push_back(minit);
pos = epos;
}
}
}
- for (int i = 0; i < cell->parameters.at(ID::RD_PORTS).as_int(); i++) {
+ int n_rd_ports = cell->parameters.at(ID::RD_PORTS).as_int();
+ int n_wr_ports = cell->parameters.at(ID::WR_PORTS).as_int();
+ Const rd_wide_continuation = is_compat ? Const(State::S0, n_rd_ports) : cell->parameters.at(ID::RD_WIDE_CONTINUATION);
+ Const wr_wide_continuation = is_compat ? Const(State::S0, n_wr_ports) : cell->parameters.at(ID::WR_WIDE_CONTINUATION);
+ for (int i = 0, ni; i < n_rd_ports; i = ni) {
+ ni = i + 1;
+ while (ni < n_rd_ports && rd_wide_continuation[ni] == State::S1)
+ ni++;
MemRd mrd;
+ mrd.wide_log2 = ceil_log2(ni - i);
+ log_assert(ni - i == (1 << mrd.wide_log2));
mrd.clk_enable = cell->parameters.at(ID::RD_CLK_ENABLE).extract(i, 1).as_bool();
mrd.clk_polarity = cell->parameters.at(ID::RD_CLK_POLARITY).extract(i, 1).as_bool();
- mrd.transparent = cell->parameters.at(ID::RD_TRANSPARENT).extract(i, 1).as_bool();
mrd.clk = cell->getPort(ID::RD_CLK).extract(i, 1);
mrd.en = cell->getPort(ID::RD_EN).extract(i, 1);
mrd.addr = cell->getPort(ID::RD_ADDR).extract(i * abits, abits);
- mrd.data = cell->getPort(ID::RD_DATA).extract(i * res.width, res.width);
+ mrd.data = cell->getPort(ID::RD_DATA).extract(i * res.width, (ni - i) * res.width);
+ if (is_compat) {
+ mrd.ce_over_srst = false;
+ mrd.arst_value = Const(State::Sx, res.width << mrd.wide_log2);
+ mrd.srst_value = Const(State::Sx, res.width << mrd.wide_log2);
+ mrd.init_value = Const(State::Sx, res.width << mrd.wide_log2);
+ mrd.arst = State::S0;
+ mrd.srst = State::S0;
+ } else {
+ mrd.ce_over_srst = cell->parameters.at(ID::RD_CE_OVER_SRST).extract(i, 1).as_bool();
+ mrd.arst_value = cell->parameters.at(ID::RD_ARST_VALUE).extract(i * res.width, (ni - i) * res.width);
+ mrd.srst_value = cell->parameters.at(ID::RD_SRST_VALUE).extract(i * res.width, (ni - i) * res.width);
+ mrd.init_value = cell->parameters.at(ID::RD_INIT_VALUE).extract(i * res.width, (ni - i) * res.width);
+ mrd.arst = cell->getPort(ID::RD_ARST).extract(i, 1);
+ mrd.srst = cell->getPort(ID::RD_SRST).extract(i, 1);
+ }
+ if (!is_compat) {
+ Const transparency_mask = cell->parameters.at(ID::RD_TRANSPARENCY_MASK).extract(i * n_wr_ports, n_wr_ports);
+ Const collision_x_mask = cell->parameters.at(ID::RD_COLLISION_X_MASK).extract(i * n_wr_ports, n_wr_ports);
+ for (int j = 0; j < n_wr_ports; j++)
+ if (wr_wide_continuation[j] != State::S1) {
+ mrd.transparency_mask.push_back(transparency_mask[j] == State::S1);
+ mrd.collision_x_mask.push_back(collision_x_mask[j] == State::S1);
+ }
+ }
res.rd_ports.push_back(mrd);
}
- for (int i = 0; i < cell->parameters.at(ID::WR_PORTS).as_int(); i++) {
+ for (int i = 0, ni; i < n_wr_ports; i = ni) {
+ ni = i + 1;
+ while (ni < n_wr_ports && wr_wide_continuation[ni] == State::S1)
+ ni++;
MemWr mwr;
+ mwr.wide_log2 = ceil_log2(ni - i);
+ log_assert(ni - i == (1 << mwr.wide_log2));
mwr.clk_enable = cell->parameters.at(ID::WR_CLK_ENABLE).extract(i, 1).as_bool();
mwr.clk_polarity = cell->parameters.at(ID::WR_CLK_POLARITY).extract(i, 1).as_bool();
mwr.clk = cell->getPort(ID::WR_CLK).extract(i, 1);
- mwr.en = cell->getPort(ID::WR_EN).extract(i * res.width, res.width);
+ mwr.en = cell->getPort(ID::WR_EN).extract(i * res.width, (ni - i) * res.width);
mwr.addr = cell->getPort(ID::WR_ADDR).extract(i * abits, abits);
- mwr.data = cell->getPort(ID::WR_DATA).extract(i * res.width, res.width);
+ mwr.data = cell->getPort(ID::WR_DATA).extract(i * res.width, (ni - i) * res.width);
+ if (!is_compat) {
+ Const priority_mask = cell->parameters.at(ID::WR_PRIORITY_MASK).extract(i * n_wr_ports, n_wr_ports);
+ for (int j = 0; j < n_wr_ports; j++)
+ if (wr_wide_continuation[j] != State::S1)
+ mwr.priority_mask.push_back(priority_mask[j] == State::S1);
+ }
res.wr_ports.push_back(mwr);
}
+ if (is_compat) {
+ for (int i = 0; i < GetSize(res.wr_ports); i++) {
+ auto &port = res.wr_ports[i];
+ port.priority_mask.resize(GetSize(res.wr_ports));
+ for (int j = 0; j < i; j++) {
+ auto &oport = res.wr_ports[j];
+ if (port.clk_enable != oport.clk_enable)
+ continue;
+ if (port.clk_enable && port.clk != oport.clk)
+ continue;
+ if (port.clk_enable && port.clk_polarity != oport.clk_polarity)
+ continue;
+ port.priority_mask[j] = true;
+ }
+ }
+ for (int i = 0; i < GetSize(res.rd_ports); i++) {
+ auto &port = res.rd_ports[i];
+ port.transparency_mask.resize(GetSize(res.wr_ports));
+ port.collision_x_mask.resize(GetSize(res.wr_ports));
+ if (!cell->parameters.at(ID::RD_TRANSPARENT).extract(i, 1).as_bool())
+ continue;
+ if (!port.clk_enable)
+ continue;
+ for (int j = 0; j < GetSize(res.wr_ports); j++) {
+ auto &wport = res.wr_ports[j];
+ if (!wport.clk_enable)
+ continue;
+ if (port.clk != wport.clk)
+ continue;
+ if (port.clk_polarity != wport.clk_polarity)
+ continue;
+ port.transparency_mask[j] = true;
+ }
+ }
+ }
+ res.check();
return res;
}
@@ -381,7 +826,7 @@ std::vector<Mem> Mem::get_all_memories(Module *module) {
res.push_back(mem_from_memory(module, it.second, index));
}
for (auto cell: module->cells()) {
- if (cell->type == ID($mem))
+ if (cell->type.in(ID($mem), ID($mem_v2)))
res.push_back(mem_from_cell(cell));
}
return res;
@@ -395,13 +840,13 @@ std::vector<Mem> Mem::get_selected_memories(Module *module) {
res.push_back(mem_from_memory(module, it.second, index));
}
for (auto cell: module->selected_cells()) {
- if (cell->type == ID($mem))
+ if (cell->type.in(ID($mem), ID($mem_v2)))
res.push_back(mem_from_cell(cell));
}
return res;
}
-Cell *Mem::extract_rdff(int idx) {
+Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
MemRd &port = rd_ports[idx];
if (!port.clk_enable)
@@ -409,28 +854,813 @@ Cell *Mem::extract_rdff(int idx) {
Cell *c;
- if (port.transparent)
+ // There are two ways to handle rdff extraction when transparency is involved:
+ //
+ // - if all of the following conditions are true, put the FF on address input:
+ //
+ // - the port has no clock enable, no reset, and no initial value
+ // - the port is transparent wrt all write ports (implying they also share
+ // the clock domain)
+ //
+ // - otherwise, put the FF on the data output, and make bypass paths for
+ // all write ports wrt which this port is transparent
+ bool trans_use_addr = true;
+ for (int i = 0; i < GetSize(wr_ports); i++)
+ if (!port.transparency_mask[i] && !wr_ports[i].removed)
+ trans_use_addr = false;
+
+ // If there are no write ports at all, we could possibly use either way; do data
+ // FF in this case.
+ if (GetSize(wr_ports) == 0)
+ trans_use_addr = false;
+
+ if (port.en != State::S1 || port.srst != State::S0 || port.arst != State::S0 || !port.init_value.is_fully_undef())
+ trans_use_addr = false;
+
+ if (trans_use_addr)
{
- SigSpec sig_q = module->addWire(stringf("%s$rdreg[%d]$q", memid.c_str(), idx), GetSize(port.addr));
- SigSpec sig_d = port.addr;
- port.addr = sig_q;
- c = module->addDffe(stringf("%s$rdreg[%d]", memid.c_str(), idx), port.clk, port.en, sig_d, sig_q, port.clk_polarity, true);
+ // Do not put a register in front of constant address bits — this is both
+ // unnecessary and will break wide ports.
+ int width = 0;
+ for (int i = 0; i < GetSize(port.addr); i++)
+ if (port.addr[i].wire)
+ width++;
+
+ if (width)
+ {
+ SigSpec sig_q = module->addWire(stringf("$%s$rdreg[%d]$q", memid.c_str(), idx), width);
+ SigSpec sig_d;
+
+ int pos = 0;
+ for (int i = 0; i < GetSize(port.addr); i++)
+ if (port.addr[i].wire) {
+ sig_d.append(port.addr[i]);
+ port.addr[i] = sig_q[pos++];
+ }
+
+ c = module->addDff(stringf("$%s$rdreg[%d]", memid.c_str(), idx), port.clk, sig_d, sig_q, port.clk_polarity);
+ } else {
+ c = nullptr;
+ }
}
else
{
- SigSpec sig_d = module->addWire(stringf("%s$rdreg[%d]$d", memid.c_str(), idx), width);
- SigSpec sig_q = port.data;
- port.data = sig_d;
- c = module->addDffe(stringf("%s$rdreg[%d]", memid.c_str(), idx), port.clk, port.en, sig_d, sig_q, port.clk_polarity, true);
+ log_assert(port.arst == State::S0 || port.srst == State::S0);
+
+ SigSpec async_d = module->addWire(stringf("$%s$rdreg[%d]$d", memid.c_str(), idx), GetSize(port.data));
+ SigSpec sig_d = async_d;
+
+ for (int i = 0; i < GetSize(wr_ports); i++) {
+ auto &wport = wr_ports[i];
+ if (wport.removed)
+ continue;
+ if (port.transparency_mask[i] || port.collision_x_mask[i]) {
+ log_assert(wport.clk_enable);
+ log_assert(wport.clk == port.clk);
+ log_assert(wport.clk_enable == port.clk_enable);
+ int min_wide_log2 = std::min(port.wide_log2, wport.wide_log2);
+ int max_wide_log2 = std::max(port.wide_log2, wport.wide_log2);
+ bool wide_write = wport.wide_log2 > port.wide_log2;
+ for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) {
+ SigSpec raddr = port.addr;
+ SigSpec waddr = wport.addr;
+ if (wide_write)
+ waddr = wport.sub_addr(sub);
+ else
+ raddr = port.sub_addr(sub);
+ SigSpec addr_eq;
+ if (raddr != waddr)
+ addr_eq = module->Eq(stringf("$%s$rdtransen[%d][%d][%d]$d", memid.c_str(), idx, i, sub), raddr, waddr);
+ int pos = 0;
+ int ewidth = width << min_wide_log2;
+ int wsub = wide_write ? sub : 0;
+ int rsub = wide_write ? 0 : sub;
+ while (pos < ewidth) {
+ int epos = pos;
+ while (epos < ewidth && wport.en[epos + wsub * width] == wport.en[pos + wsub * width])
+ epos++;
+ SigSpec cur = sig_d.extract(pos + rsub * width, epos-pos);
+ SigSpec other = port.transparency_mask[i] ? wport.data.extract(pos + wsub * width, epos-pos) : Const(State::Sx, epos-pos);
+ SigSpec cond;
+ if (raddr != waddr)
+ cond = module->And(stringf("$%s$rdtransgate[%d][%d][%d][%d]$d", memid.c_str(), idx, i, sub, pos), wport.en[pos + wsub * width], addr_eq);
+ else
+ cond = wport.en[pos + wsub * width];
+ SigSpec merged = module->Mux(stringf("$%s$rdtransmux[%d][%d][%d][%d]$d", memid.c_str(), idx, i, sub, pos), cur, other, cond);
+ sig_d.replace(pos + rsub * width, merged);
+ pos = epos;
+ }
+ }
+ }
+ }
+
+ IdString name = stringf("$%s$rdreg[%d]", memid.c_str(), idx);
+ FfData ff(module, initvals, name);
+ ff.width = GetSize(port.data);
+ ff.has_clk = true;
+ ff.sig_clk = port.clk;
+ ff.pol_clk = port.clk_polarity;
+ if (port.en != State::S1) {
+ ff.has_ce = true;
+ ff.pol_ce = true;
+ ff.sig_ce = port.en;
+ }
+ if (port.arst != State::S0) {
+ ff.has_arst = true;
+ ff.pol_arst = true;
+ ff.sig_arst = port.arst;
+ ff.val_arst = port.arst_value;
+ }
+ if (port.srst != State::S0) {
+ ff.has_srst = true;
+ ff.pol_srst = true;
+ ff.sig_srst = port.srst;
+ ff.val_srst = port.srst_value;
+ ff.ce_over_srst = ff.has_ce && port.ce_over_srst;
+ }
+ ff.sig_d = sig_d;
+ ff.sig_q = port.data;
+ ff.val_init = port.init_value;
+ port.data = async_d;
+ c = ff.emit();
}
- log("Extracted %s FF from read port %d of %s.%s: %s\n", port.transparent ? "addr" : "data",
+ if (c)
+ log("Extracted %s FF from read port %d of %s.%s: %s\n", trans_use_addr ? "addr" : "data",
idx, log_id(module), log_id(memid), log_id(c));
port.en = State::S1;
port.clk = State::S0;
+ port.arst = State::S0;
+ port.srst = State::S0;
port.clk_enable = false;
port.clk_polarity = true;
+ port.ce_over_srst = false;
+ port.arst_value = Const(State::Sx, GetSize(port.data));
+ port.srst_value = Const(State::Sx, GetSize(port.data));
+ port.init_value = Const(State::Sx, GetSize(port.data));
+
+ for (int i = 0; i < GetSize(wr_ports); i++) {
+ port.transparency_mask[i] = false;
+ port.collision_x_mask[i] = false;
+ }
return c;
}
+
+void Mem::narrow() {
+ // NOTE: several passes depend on this function not modifying
+ // the design at all until (and unless) emit() is called.
+ // Be careful to preserve this.
+ std::vector<MemRd> new_rd_ports;
+ std::vector<MemWr> new_wr_ports;
+ std::vector<std::pair<int, int>> new_rd_map;
+ std::vector<std::pair<int, int>> new_wr_map;
+ for (int i = 0; i < GetSize(rd_ports); i++) {
+ auto &port = rd_ports[i];
+ for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
+ new_rd_map.push_back(std::make_pair(i, sub));
+ }
+ }
+ for (int i = 0; i < GetSize(wr_ports); i++) {
+ auto &port = wr_ports[i];
+ for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
+ new_wr_map.push_back(std::make_pair(i, sub));
+ }
+ }
+ for (auto &it : new_rd_map) {
+ MemRd &orig = rd_ports[it.first];
+ MemRd port = orig;
+ if (it.second != 0)
+ port.cell = nullptr;
+ if (port.wide_log2) {
+ port.data = port.data.extract(it.second * width, width);
+ port.init_value = port.init_value.extract(it.second * width, width);
+ port.arst_value = port.arst_value.extract(it.second * width, width);
+ port.srst_value = port.srst_value.extract(it.second * width, width);
+ port.addr = port.sub_addr(it.second);
+ port.wide_log2 = 0;
+ }
+ port.transparency_mask.clear();
+ port.collision_x_mask.clear();
+ for (auto &it2 : new_wr_map)
+ port.transparency_mask.push_back(orig.transparency_mask[it2.first]);
+ for (auto &it2 : new_wr_map)
+ port.collision_x_mask.push_back(orig.collision_x_mask[it2.first]);
+ new_rd_ports.push_back(port);
+ }
+ for (auto &it : new_wr_map) {
+ MemWr &orig = wr_ports[it.first];
+ MemWr port = orig;
+ if (it.second != 0)
+ port.cell = nullptr;
+ if (port.wide_log2) {
+ port.data = port.data.extract(it.second * width, width);
+ port.en = port.en.extract(it.second * width, width);
+ port.addr = port.sub_addr(it.second);
+ port.wide_log2 = 0;
+ }
+ port.priority_mask.clear();
+ for (auto &it2 : new_wr_map)
+ port.priority_mask.push_back(orig.priority_mask[it2.first]);
+ new_wr_ports.push_back(port);
+ }
+ std::swap(rd_ports, new_rd_ports);
+ std::swap(wr_ports, new_wr_ports);
+}
+
+void Mem::emulate_priority(int idx1, int idx2, FfInitVals *initvals)
+{
+ auto &port1 = wr_ports[idx1];
+ auto &port2 = wr_ports[idx2];
+ if (!port2.priority_mask[idx1])
+ return;
+ for (int i = 0; i < GetSize(rd_ports); i++) {
+ auto &rport = rd_ports[i];
+ if (rport.removed)
+ continue;
+ if (rport.transparency_mask[idx1] && !(rport.transparency_mask[idx2] || rport.collision_x_mask[idx2]))
+ emulate_transparency(idx1, i, initvals);
+ }
+ int min_wide_log2 = std::min(port1.wide_log2, port2.wide_log2);
+ int max_wide_log2 = std::max(port1.wide_log2, port2.wide_log2);
+ bool wide1 = port1.wide_log2 > port2.wide_log2;
+ for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) {
+ SigSpec addr1 = port1.addr;
+ SigSpec addr2 = port2.addr;
+ if (wide1)
+ addr1 = port1.sub_addr(sub);
+ else
+ addr2 = port2.sub_addr(sub);
+ SigSpec addr_eq = module->Eq(NEW_ID, addr1, addr2);
+ int ewidth = width << min_wide_log2;
+ int sub1 = wide1 ? sub : 0;
+ int sub2 = wide1 ? 0 : sub;
+ dict<std::pair<SigBit, SigBit>, SigBit> cache;
+ for (int pos = 0; pos < ewidth; pos++) {
+ SigBit &en1 = port1.en[pos + sub1 * width];
+ SigBit &en2 = port2.en[pos + sub2 * width];
+ std::pair<SigBit, SigBit> key(en1, en2);
+ if (cache.count(key)) {
+ en1 = cache[key];
+ } else {
+ SigBit active2 = module->And(NEW_ID, addr_eq, en2);
+ SigBit nactive2 = module->Not(NEW_ID, active2);
+ en1 = cache[key] = module->And(NEW_ID, en1, nactive2);
+ }
+ }
+ }
+ port2.priority_mask[idx1] = false;
+}
+
+void Mem::emulate_transparency(int widx, int ridx, FfInitVals *initvals) {
+ auto &wport = wr_ports[widx];
+ auto &rport = rd_ports[ridx];
+ log_assert(rport.transparency_mask[widx]);
+ // If other write ports have priority over this one, emulate their transparency too.
+ for (int i = GetSize(wr_ports) - 1; i > widx; i--) {
+ if (wr_ports[i].removed)
+ continue;
+ if (rport.transparency_mask[i] && wr_ports[i].priority_mask[widx])
+ emulate_transparency(i, ridx, initvals);
+ }
+ int min_wide_log2 = std::min(rport.wide_log2, wport.wide_log2);
+ int max_wide_log2 = std::max(rport.wide_log2, wport.wide_log2);
+ bool wide_write = wport.wide_log2 > rport.wide_log2;
+ // The write data FF doesn't need full reset/init behavior, as it'll be masked by
+ // the mux whenever this would be relevant. It does, however, need to have the same
+ // clock enable signal as the read port.
+ SigSpec wdata_q = module->addWire(NEW_ID, GetSize(wport.data));
+ module->addDffe(NEW_ID, rport.clk, rport.en, wport.data, wdata_q, rport.clk_polarity, true);
+ for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) {
+ SigSpec raddr = rport.addr;
+ SigSpec waddr = wport.addr;
+ for (int j = min_wide_log2; j < max_wide_log2; j++)
+ if (wide_write)
+ waddr = wport.sub_addr(sub);
+ else
+ raddr = rport.sub_addr(sub);
+ SigSpec addr_eq;
+ if (raddr != waddr)
+ addr_eq = module->Eq(NEW_ID, raddr, waddr);
+ int pos = 0;
+ int ewidth = width << min_wide_log2;
+ int wsub = wide_write ? sub : 0;
+ int rsub = wide_write ? 0 : sub;
+ SigSpec rdata_a = module->addWire(NEW_ID, ewidth);
+ while (pos < ewidth) {
+ int epos = pos;
+ while (epos < ewidth && wport.en[epos + wsub * width] == wport.en[pos + wsub * width])
+ epos++;
+ SigSpec cond;
+ if (raddr != waddr)
+ cond = module->And(NEW_ID, wport.en[pos + wsub * width], addr_eq);
+ else
+ cond = wport.en[pos + wsub * width];
+ SigSpec cond_q = module->addWire(NEW_ID);
+ // The FF for storing the bypass enable signal must be carefully
+ // constructed to preserve the overall init/reset/enable behavior
+ // of the whole port.
+ FfData ff(module, initvals, NEW_ID);
+ ff.width = 1;
+ ff.sig_q = cond_q;
+ ff.sig_d = cond;
+ ff.has_clk = true;
+ ff.sig_clk = rport.clk;
+ ff.pol_clk = rport.clk_polarity;
+ if (rport.en != State::S1) {
+ ff.has_ce = true;
+ ff.sig_ce = rport.en;
+ ff.pol_ce = true;
+ }
+ if (rport.arst != State::S0) {
+ ff.has_arst = true;
+ ff.sig_arst = rport.arst;
+ ff.pol_arst = true;
+ ff.val_arst = State::S0;
+ }
+ if (rport.srst != State::S0) {
+ ff.has_srst = true;
+ ff.sig_srst = rport.srst;
+ ff.pol_srst = true;
+ ff.val_srst = State::S0;
+ ff.ce_over_srst = rport.ce_over_srst;
+ }
+ if (!rport.init_value.is_fully_undef())
+ ff.val_init = State::S0;
+ else
+ ff.val_init = State::Sx;
+ ff.emit();
+ // And the final bypass mux.
+ SigSpec cur = rdata_a.extract(pos, epos-pos);
+ SigSpec other = wdata_q.extract(pos + wsub * width, epos-pos);
+ SigSpec dest = rport.data.extract(pos + rsub * width, epos-pos);
+ module->addMux(NEW_ID, cur, other, cond_q, dest);
+ pos = epos;
+ }
+ rport.data.replace(rsub * width, rdata_a);
+ }
+ rport.transparency_mask[widx] = false;
+ rport.collision_x_mask[widx] = true;
+}
+
+void Mem::prepare_wr_merge(int idx1, int idx2, FfInitVals *initvals) {
+ log_assert(idx1 < idx2);
+ auto &port1 = wr_ports[idx1];
+ auto &port2 = wr_ports[idx2];
+ // If port 2 has priority over a port before port 1, make port 1 have priority too.
+ for (int i = 0; i < idx1; i++)
+ if (port2.priority_mask[i])
+ port1.priority_mask[i] = true;
+ // If port 2 has priority over a port after port 1, emulate it.
+ for (int i = idx1 + 1; i < idx2; i++)
+ if (port2.priority_mask[i] && !wr_ports[i].removed)
+ emulate_priority(i, idx2, initvals);
+ // If some port had priority over port 2, make it have priority over the merged port too.
+ for (int i = idx2 + 1; i < GetSize(wr_ports); i++) {
+ auto &oport = wr_ports[i];
+ if (oport.priority_mask[idx2])
+ oport.priority_mask[idx1] = true;
+ }
+ // Make sure all read ports have identical collision/transparency behavior wrt both
+ // ports.
+ for (int i = 0; i < GetSize(rd_ports); i++) {
+ auto &rport = rd_ports[i];
+ if (rport.removed)
+ continue;
+ // If collision already undefined with both ports, it's fine.
+ if (rport.collision_x_mask[idx1] && rport.collision_x_mask[idx2])
+ continue;
+ // If one port has undefined collision, change it to the behavior
+ // of the other port.
+ if (rport.collision_x_mask[idx1]) {
+ rport.collision_x_mask[idx1] = false;
+ rport.transparency_mask[idx1] = rport.transparency_mask[idx2];
+ continue;
+ }
+ if (rport.collision_x_mask[idx2]) {
+ rport.collision_x_mask[idx2] = false;
+ rport.transparency_mask[idx2] = rport.transparency_mask[idx1];
+ continue;
+ }
+ // If transparent with both ports, also fine.
+ if (rport.transparency_mask[idx1] && rport.transparency_mask[idx2])
+ continue;
+ // If transparent with only one, emulate it, and remove the collision-X
+ // flag that emulate_transparency will set (to align with the other port).
+ if (rport.transparency_mask[idx1]) {
+ emulate_transparency(i, idx1, initvals);
+ rport.collision_x_mask[idx1] = false;
+ continue;
+ }
+ if (rport.transparency_mask[idx2]) {
+ emulate_transparency(i, idx2, initvals);
+ rport.collision_x_mask[idx2] = false;
+ continue;
+ }
+ // If we got here, it's transparent with neither port, which is fine.
+ }
+}
+
+void Mem::prepare_rd_merge(int idx1, int idx2, FfInitVals *initvals) {
+ auto &port1 = rd_ports[idx1];
+ auto &port2 = rd_ports[idx2];
+ // Note that going through write ports in order is important, since
+ // emulating transparency of a write port can change transparency
+ // mask for higher-numbered ports (due to transitive transparency
+ // emulation needed because of write port priority).
+ for (int i = 0; i < GetSize(wr_ports); i++) {
+ if (wr_ports[i].removed)
+ continue;
+ // Both ports undefined, OK.
+ if (port1.collision_x_mask[i] && port2.collision_x_mask[i])
+ continue;
+ // Only one port undefined — change its behavior
+ // to align with the other port.
+ if (port1.collision_x_mask[i]) {
+ port1.collision_x_mask[i] = false;
+ port1.transparency_mask[i] = port2.transparency_mask[i];
+ continue;
+ }
+ if (port2.collision_x_mask[i]) {
+ port2.collision_x_mask[i] = false;
+ port2.transparency_mask[i] = port1.transparency_mask[i];
+ continue;
+ }
+ // Both ports transparent, OK.
+ if (port1.transparency_mask[i] && port2.transparency_mask[i])
+ continue;
+ // Only one port transparent — emulate transparency
+ // on the other.
+ if (port1.transparency_mask[i]) {
+ emulate_transparency(i, idx1, initvals);
+ port1.collision_x_mask[i] = false;
+ continue;
+ }
+ if (port2.transparency_mask[i]) {
+ emulate_transparency(i, idx2, initvals);
+ port2.collision_x_mask[i] = false;
+ continue;
+ }
+ // No ports transparent, OK.
+ }
+
+}
+
+void Mem::widen_prep(int wide_log2) {
+ // Make sure start_offset and size are aligned to the port width,
+ // adjust if necessary.
+ int mask = ((1 << wide_log2) - 1);
+ int delta = start_offset & mask;
+ start_offset -= delta;
+ size += delta;
+ if (size & mask) {
+ size |= mask;
+ size++;
+ }
+}
+
+void Mem::widen_wr_port(int idx, int wide_log2) {
+ widen_prep(wide_log2);
+ auto &port = wr_ports[idx];
+ log_assert(port.wide_log2 <= wide_log2);
+ if (port.wide_log2 < wide_log2) {
+ SigSpec new_data, new_en;
+ SigSpec addr_lo = port.addr.extract(0, wide_log2);
+ for (int sub = 0; sub < (1 << wide_log2); sub += (1 << port.wide_log2))
+ {
+ Const cur_addr_lo(sub, wide_log2);
+ if (addr_lo == cur_addr_lo) {
+ // Always writes to this subword.
+ new_data.append(port.data);
+ new_en.append(port.en);
+ } else if (addr_lo.is_fully_const()) {
+ // Never writes to this subword.
+ new_data.append(Const(State::Sx, GetSize(port.data)));
+ new_en.append(Const(State::S0, GetSize(port.data)));
+ } else {
+ // May or may not write to this subword.
+ new_data.append(port.data);
+ SigSpec addr_eq = module->Eq(NEW_ID, addr_lo, cur_addr_lo);
+ SigSpec en = module->Mux(NEW_ID, Const(State::S0, GetSize(port.data)), port.en, addr_eq);
+ new_en.append(en);
+ }
+ }
+ port.addr.replace(port.wide_log2, Const(State::S0, wide_log2 - port.wide_log2));
+ port.data = new_data;
+ port.en = new_en;
+ 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 6d727e71d..ae87b1285 100644
--- a/kernel/mem.h
+++ b/kernel/mem.h
@@ -21,38 +21,76 @@
#define MEM_H
#include "kernel/yosys.h"
+#include "kernel/ffinit.h"
YOSYS_NAMESPACE_BEGIN
-struct MemRd {
- dict<IdString, Const> attributes;
+struct MemRd : RTLIL::AttrObject {
+ bool removed;
Cell *cell;
- bool clk_enable, clk_polarity;
- bool transparent;
- SigSpec clk, en, addr, data;
- MemRd() : cell(nullptr) {}
+ int wide_log2;
+ bool clk_enable, clk_polarity, ce_over_srst;
+ Const arst_value, srst_value, init_value;
+ // One bit for every write port, true iff simultanous read on this
+ // port and write on the other port will bypass the written data
+ // to this port's output (default behavior is to read old value).
+ // Can only be set for write ports that have the same clock domain.
+ std::vector<bool> transparency_mask;
+ // One bit for every write port, true iff simultanous read on this
+ // port and write on the other port will return an all-X (don't care)
+ // value. Mutually exclusive with transparency_mask.
+ // Can only be set for write ports that have the same clock domain.
+ // For optimization purposes, this will also be set if we can
+ // determine that the two ports can never be active simultanously
+ // (making the above vacuously true).
+ std::vector<bool> collision_x_mask;
+ SigSpec clk, en, arst, srst, addr, data;
+
+ MemRd() : removed(false), cell(nullptr) {}
+
+ // Returns the address of given subword index accessed by this port.
+ SigSpec sub_addr(int sub) {
+ SigSpec res = addr;
+ for (int i = 0; i < wide_log2; i++)
+ res[i] = State(sub >> i & 1);
+ return res;
+ }
};
-struct MemWr {
- dict<IdString, Const> attributes;
+struct MemWr : RTLIL::AttrObject {
+ bool removed;
Cell *cell;
+ int wide_log2;
bool clk_enable, clk_polarity;
+ std::vector<bool> priority_mask;
SigSpec clk, en, addr, data;
- MemWr() : cell(nullptr) {}
+
+ MemWr() : removed(false), cell(nullptr) {}
+
+ // Returns the address of given subword index accessed by this port.
+ SigSpec sub_addr(int sub) {
+ SigSpec res = addr;
+ for (int i = 0; i < wide_log2; i++)
+ 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 {
- dict<IdString, Const> attributes;
+struct MemInit : RTLIL::AttrObject {
+ bool removed;
Cell *cell;
Const addr;
Const data;
- MemInit() : cell(nullptr) {}
+ Const en;
+ MemInit() : removed(false), cell(nullptr) {}
};
-struct Mem {
+struct Mem : RTLIL::AttrObject {
Module *module;
IdString memid;
- dict<IdString, Const> attributes;
bool packed;
RTLIL::Memory *mem;
Cell *cell;
@@ -61,15 +99,128 @@ struct Mem {
std::vector<MemRd> rd_ports;
std::vector<MemWr> wr_ports;
+ // Removes this memory from the module. The data in helper structures
+ // is unaffected except for the cell/mem fields.
void remove();
+
+ // Commits all changes in helper structures into the module — ports and
+ // inits marked as removed are actually removed, new ports/inits create
+ // new cells, modified port/inits are commited into their existing
+ // cells. Note that this reindexes the ports and inits array (actually
+ // removing the ports/inits marked as removed).
void emit();
- void remove_wr_port(int idx);
- void remove_rd_port(int idx);
+
+ // Marks all inits as removed.
void clear_inits();
+
+ // Coalesces inits: whenever two inits have overlapping or touching
+ // address ranges, they are combined into one, with the higher-priority
+ // one's data overwriting the other. Running this results in
+ // an inits list equivalent to the original, in which all entries
+ // cover disjoint (and non-touching) address ranges, and all enable
+ // masks are all-1.
+ void coalesce_inits();
+
+ // Checks consistency of this memory and all its ports/inits, using
+ // log_assert.
+ void check();
+
+ // Gathers all initialization data into a single big const covering
+ // the whole memory. For all non-initialized bits, Sx will be returned.
Const get_init_data() const;
+
+ // Constructs and returns the helper structures for all memories
+ // in a module.
static std::vector<Mem> get_all_memories(Module *module);
+
+ // Constructs and returns the helper structures for all selected
+ // memories in a module.
static std::vector<Mem> get_selected_memories(Module *module);
- Cell *extract_rdff(int idx);
+
+ // Converts a synchronous read port into an asynchronous one by
+ // extracting the data (or, in some rare cases, address) register
+ // into a separate cell, together with any soft-transparency
+ // logic necessary to preserve its semantics. Returns the created
+ // register cell, if any. Note that in some rare cases this function
+ // may succeed and perform a conversion without creating a new
+ // register — a nullptr result doesn't imply nothing was done.
+ Cell *extract_rdff(int idx, FfInitVals *initvals);
+
+ // Splits all wide ports in this memory into equivalent narrow ones.
+ // This function performs no modifications at all to the actual
+ // netlist unless and until emit() is called.
+ void narrow();
+
+ // If write port idx2 currently has priority over write port idx1,
+ // inserts extra logic on idx1's enable signal to disable writes
+ // when idx2 is writing to the same address, then removes the priority
+ // from the priority mask. If there is a memory port that is
+ // transparent with idx1, but not with idx2, that port is converted
+ // to use soft transparency logic.
+ void emulate_priority(int idx1, int idx2, FfInitVals *initvals);
+
+ // Creates soft-transparency logic on read port ridx, bypassing the
+ // data from write port widx. Should only be called when ridx is
+ // transparent wrt widx in the first place. Once we're done, the
+ // transparency_mask bit will be cleared, and the collision_x_mask
+ // bit will be set instead (since whatever value is read will be
+ // replaced by the soft transparency logic).
+ void emulate_transparency(int widx, int ridx, FfInitVals *initvals);
+
+ // Prepares for merging write port idx2 into idx1 (where idx1 < idx2).
+ // Specifically, takes care of priority masks: any priority relations
+ // that idx2 had are replicated onto idx1, unless they conflict with
+ // priorities already present on idx1, in which case emulate_priority
+ // is called. Likewise, ensures transparency and undefined collision
+ // masks of all read ports have the same values for both ports,
+ // calling emulate_transparency if necessary.
+ void prepare_wr_merge(int idx1, int idx2, FfInitVals *initvals);
+
+ // Prepares for merging read port idx2 into idx1.
+ // Specifically, makes sure the transparency and undefined collision
+ // masks of both ports are equal, by changing undefined behavior
+ // of one port to the other's defined behavior, or by calling
+ // emulate_transparency if necessary.
+ void prepare_rd_merge(int idx1, int idx2, FfInitVals *initvals);
+
+ // Prepares the memory for widening a port to a given width. This
+ // involves ensuring that start_offset and size are aligned to the
+ // target width.
+ void widen_prep(int wide_log2);
+
+ // Widens a write port up to a given width. The newly port is
+ // equivalent to the original, made by replicating enable/data bits
+ // and masking enable bits with decoders on the low part of the
+ // 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/modtools.h b/kernel/modtools.h
index 29c510059..4cbaf78d0 100644
--- a/kernel/modtools.h
+++ b/kernel/modtools.h
@@ -1,7 +1,7 @@
/* -*- c++ -*-
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -380,9 +380,11 @@ struct ModWalker
}
}
- ModWalker(RTLIL::Design *design) : design(design), module(NULL)
+ ModWalker(RTLIL::Design *design, RTLIL::Module *module = nullptr) : design(design), module(NULL)
{
- ct.setup(design);
+ ct.setup(design);
+ if (module)
+ setup(module);
}
void setup(RTLIL::Module *module, CellTypes *filter_ct = NULL)
@@ -395,6 +397,8 @@ struct ModWalker
signal_consumers.clear();
signal_inputs.clear();
signal_outputs.clear();
+ cell_inputs.clear();
+ cell_outputs.clear();
for (auto &it : module->wires_)
add_wire(it.second);
diff --git a/kernel/qcsat.cc b/kernel/qcsat.cc
new file mode 100644
index 000000000..aaee984fb
--- /dev/null
+++ b/kernel/qcsat.cc
@@ -0,0 +1,102 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2021 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/qcsat.h"
+
+USING_YOSYS_NAMESPACE
+
+std::vector<int> QuickConeSat::importSig(SigSpec sig)
+{
+ sig = modwalker.sigmap(sig);
+ for (auto bit : sig)
+ bits_queue.insert(bit);
+ return satgen.importSigSpec(sig);
+}
+
+int QuickConeSat::importSigBit(SigBit bit)
+{
+ bit = modwalker.sigmap(bit);
+ bits_queue.insert(bit);
+ return satgen.importSigBit(bit);
+}
+
+void QuickConeSat::prepare()
+{
+ while (!bits_queue.empty())
+ {
+ pool<ModWalker::PortBit> portbits;
+ modwalker.get_drivers(portbits, bits_queue);
+
+ for (auto bit : bits_queue)
+ if (bit.wire && bit.wire->get_bool_attribute(ID::onehot) && !imported_onehot.count(bit.wire))
+ {
+ std::vector<int> bits = satgen.importSigSpec(bit.wire);
+ for (int i : bits)
+ for (int j : bits)
+ if (i != j)
+ ez->assume(ez->NOT(i), j);
+ imported_onehot.insert(bit.wire);
+ }
+
+ bits_queue.clear();
+
+ for (auto &pbit : portbits)
+ {
+ if (imported_cells.count(pbit.cell))
+ continue;
+ if (cell_complexity(pbit.cell) > max_cell_complexity)
+ continue;
+ if (max_cell_outs && GetSize(modwalker.cell_outputs[pbit.cell]) > max_cell_outs)
+ continue;
+ auto &inputs = modwalker.cell_inputs[pbit.cell];
+ bits_queue.insert(inputs.begin(), inputs.end());
+ satgen.importCell(pbit.cell);
+ imported_cells.insert(pbit.cell);
+ }
+
+ if (max_cell_count && GetSize(imported_cells) > max_cell_count)
+ break;
+ }
+}
+
+int QuickConeSat::cell_complexity(RTLIL::Cell *cell)
+{
+ if (cell->type.in(ID($concat), ID($slice), ID($pos), ID($_BUF_)))
+ return 0;
+ if (cell->type.in(ID($not), ID($and), ID($or), ID($xor), ID($xnor),
+ ID($reduce_and), ID($reduce_or), ID($reduce_xor),
+ 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($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_),
+ ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_)))
+ return 1;
+ if (cell->type.in(ID($neg), ID($add), ID($sub), ID($alu), ID($lcu),
+ ID($lt), ID($le), ID($gt), ID($ge)))
+ return 2;
+ if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx)))
+ return 3;
+ if (cell->type.in(ID($mul), ID($macc), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow)))
+ return 4;
+ // Unknown cell.
+ return 5;
+}
diff --git a/kernel/qcsat.h b/kernel/qcsat.h
new file mode 100644
index 000000000..e4d3c3c5d
--- /dev/null
+++ b/kernel/qcsat.h
@@ -0,0 +1,76 @@
+/* -*- c++ -*-
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2021 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.
+ *
+ */
+
+#ifndef QCSAT_H
+#define QCSAT_H
+
+#include "kernel/satgen.h"
+#include "kernel/modtools.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+// This is a helper class meant for easy construction of quick SAT queries
+// to a combinatorial input cone of some set of signals, meant for SAT-based
+// optimizations. Various knobs are provided to set just how much of the
+// cone should be included in the model — since this class is meant for
+// optimization, it should not be a correctness problem when some cells are
+// skipped and the solver spuriously returns SAT with a solution that
+// cannot exist in reality due to skipped constraints (ie. only UNSAT results
+// from this class should be considered binding).
+struct QuickConeSat {
+ ModWalker &modwalker;
+ ezSatPtr ez;
+ SatGen satgen;
+
+ // The effort level knobs.
+
+ // The maximum "complexity level" of cells that will be imported.
+ // - 1: bitwise operations, muxes, equality comparisons, lut, sop, fa
+ // - 2: addition, subtraction, greater/less than comparisons, lcu
+ // - 3: shifts
+ // - 4: multiplication, division, power
+ int max_cell_complexity = 2;
+ // The maximum number of cells to import, or 0 for no limit.
+ int max_cell_count = 0;
+ // If non-0, skip importing cells with more than this number of output bits.
+ int max_cell_outs = 0;
+
+ // Internal state.
+ pool<RTLIL::Cell*> imported_cells;
+ pool<RTLIL::Wire*> imported_onehot;
+ pool<RTLIL::SigBit> bits_queue;
+
+ QuickConeSat(ModWalker &modwalker) : modwalker(modwalker), ez(), satgen(ez.get(), &modwalker.sigmap) {}
+
+ // Imports a signal into the SAT solver, queues its input cone to be
+ // imported in the next prepare() call.
+ std::vector<int> importSig(SigSpec sig);
+ int importSigBit(SigBit bit);
+
+ // Imports the input cones of all previously importSig'd signals into
+ // the SAT solver.
+ void prepare();
+
+ // Returns the "complexity level" of a given cell.
+ static int cell_complexity(RTLIL::Cell *cell);
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/register.cc b/kernel/register.cc
index 34735a608..226963fda 100644
--- a/kernel/register.cc
+++ b/kernel/register.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/kernel/register.h b/kernel/register.h
index 5cd849082..15750af2a 100644
--- a/kernel/register.h
+++ b/kernel/register.h
@@ -1,7 +1,7 @@
/* -*- c++ -*-
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index 1faf376e7..a89edd992 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -20,6 +20,7 @@
#include "kernel/yosys.h"
#include "kernel/macc.h"
#include "kernel/celltypes.h"
+#include "kernel/binding.h"
#include "frontends/verilog/verilog_frontend.h"
#include "frontends/verilog/preproc.h"
#include "backends/rtlil/rtlil_backend.h"
@@ -57,6 +58,8 @@ const pool<IdString> &RTLIL::builtin_ff_cell_types() {
ID($dffsre),
ID($adff),
ID($adffe),
+ ID($aldff),
+ ID($aldffe),
ID($sdff),
ID($sdffe),
ID($sdffce),
@@ -117,6 +120,18 @@ const pool<IdString> &RTLIL::builtin_ff_cell_types() {
ID($_DFFE_PP0P_),
ID($_DFFE_PP1N_),
ID($_DFFE_PP1P_),
+ ID($_ALDFF_NN_),
+ ID($_ALDFF_NP_),
+ ID($_ALDFF_PN_),
+ ID($_ALDFF_PP_),
+ ID($_ALDFFE_NNN_),
+ ID($_ALDFFE_NNP_),
+ ID($_ALDFFE_NPN_),
+ ID($_ALDFFE_NPP_),
+ ID($_ALDFFE_PNN_),
+ ID($_ALDFFE_PNP_),
+ ID($_ALDFFE_PPN_),
+ ID($_ALDFFE_PPP_),
ID($_SDFF_NN0_),
ID($_SDFF_NN1_),
ID($_SDFF_NP0_),
@@ -363,6 +378,26 @@ bool RTLIL::Const::is_fully_undef() const
return true;
}
+bool RTLIL::Const::is_onehot(int *pos) const
+{
+ cover("kernel.rtlil.const.is_onehot");
+
+ bool found = false;
+ for (int i = 0; i < GetSize(*this); i++) {
+ auto &bit = bits[i];
+ if (bit != RTLIL::State::S0 && bit != RTLIL::State::S1)
+ return false;
+ if (bit == RTLIL::State::S1) {
+ if (found)
+ return false;
+ if (pos)
+ *pos = i;
+ found = true;
+ }
+ }
+ return found;
+}
+
bool RTLIL::AttrObject::has_attribute(RTLIL::IdString id) const
{
return attributes.count(id);
@@ -445,6 +480,35 @@ vector<string> RTLIL::AttrObject::get_hdlname_attribute() const
return split_tokens(get_string_attribute(ID::hdlname), " ");
}
+void RTLIL::AttrObject::set_intvec_attribute(RTLIL::IdString id, const vector<int> &data)
+{
+ std::stringstream attrval;
+ for (auto &i : data) {
+ if (attrval.tellp() > 0)
+ attrval << " ";
+ attrval << i;
+ }
+ attributes[id] = RTLIL::Const(attrval.str());
+}
+
+vector<int> RTLIL::AttrObject::get_intvec_attribute(RTLIL::IdString id) const
+{
+ vector<int> data;
+ auto it = attributes.find(id);
+ if (it != attributes.end())
+ for (const auto &s : split_tokens(attributes.at(id).decode_string())) {
+ char *end = nullptr;
+ errno = 0;
+ long value = strtol(s.c_str(), &end, 10);
+ if (end != s.c_str() + s.size())
+ log_cmd_error("Literal for intvec attribute has invalid format");
+ if (errno == ERANGE || value < INT_MIN || value > INT_MAX)
+ log_cmd_error("Literal for intvec attribute is out of range");
+ data.push_back(value);
+ }
+ return data;
+}
+
bool RTLIL::Selection::selected_module(RTLIL::IdString mod_name) const
{
if (full_selection)
@@ -551,8 +615,10 @@ RTLIL::Design::Design()
RTLIL::Design::~Design()
{
- for (auto it = modules_.begin(); it != modules_.end(); ++it)
- delete it->second;
+ for (auto &pr : modules_)
+ delete pr.second;
+ for (auto n : bindings_)
+ delete n;
for (auto n : verilog_packages)
delete n;
for (auto n : verilog_globals)
@@ -580,6 +646,11 @@ RTLIL::Module *RTLIL::Design::module(RTLIL::IdString name)
return modules_.count(name) ? modules_.at(name) : NULL;
}
+const RTLIL::Module *RTLIL::Design::module(RTLIL::IdString name) const
+{
+ return modules_.count(name) ? modules_.at(name) : NULL;
+}
+
RTLIL::Module *RTLIL::Design::top_module()
{
RTLIL::Module *module = nullptr;
@@ -611,9 +682,16 @@ void RTLIL::Design::add(RTLIL::Module *module)
}
}
+void RTLIL::Design::add(RTLIL::Binding *binding)
+{
+ log_assert(binding != nullptr);
+ bindings_.push_back(binding);
+}
+
RTLIL::Module *RTLIL::Design::addModule(RTLIL::IdString name)
{
- log_assert(modules_.count(name) == 0);
+ if (modules_.count(name) != 0)
+ log_error("Attempted to add new module named '%s', but a module by that name already exists\n", name.c_str());
log_assert(refcount_modules_ == 0);
RTLIL::Module *module = new RTLIL::Module;
@@ -807,12 +885,12 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_whole_modules() const
return result;
}
-std::vector<RTLIL::Module*> RTLIL::Design::selected_whole_modules_warn() const
+std::vector<RTLIL::Module*> RTLIL::Design::selected_whole_modules_warn(bool include_wb) const
{
std::vector<RTLIL::Module*> result;
result.reserve(modules_.size());
for (auto &it : modules_)
- if (it.second->get_blackbox_attribute())
+ if (it.second->get_blackbox_attribute(include_wb))
continue;
else if (selected_whole_module(it.first))
result.push_back(it.second);
@@ -838,14 +916,16 @@ RTLIL::Module::Module()
RTLIL::Module::~Module()
{
- for (auto it = wires_.begin(); it != wires_.end(); ++it)
- delete it->second;
- for (auto it = memories.begin(); it != memories.end(); ++it)
- delete it->second;
- for (auto it = cells_.begin(); it != cells_.end(); ++it)
- delete it->second;
- for (auto it = processes.begin(); it != processes.end(); ++it)
- delete it->second;
+ for (auto &pr : wires_)
+ delete pr.second;
+ for (auto &pr : memories)
+ delete pr.second;
+ for (auto &pr : cells_)
+ delete pr.second;
+ for (auto &pr : processes)
+ delete pr.second;
+ for (auto binding : bindings_)
+ delete binding;
#ifdef WITH_PYTHON
RTLIL::Module::get_all_modules()->erase(hashidx_);
#endif
@@ -885,9 +965,14 @@ void RTLIL::Module::makeblackbox()
set_bool_attribute(ID::blackbox);
}
-void RTLIL::Module::reprocess_module(RTLIL::Design *, const dict<RTLIL::IdString, RTLIL::Module *> &)
+void RTLIL::Module::expand_interfaces(RTLIL::Design *, const dict<RTLIL::IdString, RTLIL::Module *> &)
{
- log_error("Cannot reprocess_module module `%s' !\n", id2cstr(name));
+ log_error("Class doesn't support expand_interfaces (module: `%s')!\n", id2cstr(name));
+}
+
+bool RTLIL::Module::reprocess_if_necessary(RTLIL::Design *)
+{
+ return false;
}
RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, const dict<RTLIL::IdString, RTLIL::Const> &, bool mayfail)
@@ -1166,6 +1251,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));
@@ -1300,6 +1401,32 @@ namespace {
return;
}
+ if (cell->type == ID($aldff)) {
+ param_bool(ID::CLK_POLARITY);
+ param_bool(ID::ALOAD_POLARITY);
+ port(ID::CLK, 1);
+ port(ID::ALOAD, 1);
+ port(ID::D, param(ID::WIDTH));
+ port(ID::AD, param(ID::WIDTH));
+ port(ID::Q, param(ID::WIDTH));
+ check_expected();
+ return;
+ }
+
+ if (cell->type == ID($aldffe)) {
+ param_bool(ID::CLK_POLARITY);
+ param_bool(ID::EN_POLARITY);
+ param_bool(ID::ALOAD_POLARITY);
+ port(ID::CLK, 1);
+ port(ID::EN, 1);
+ port(ID::ALOAD, 1);
+ port(ID::D, param(ID::WIDTH));
+ port(ID::AD, param(ID::WIDTH));
+ port(ID::Q, param(ID::WIDTH));
+ check_expected();
+ return;
+ }
+
if (cell->type == ID($dlatch)) {
param_bool(ID::EN_POLARITY);
port(ID::EN, 1);
@@ -1366,6 +1493,26 @@ namespace {
return;
}
+ if (cell->type == ID($memrd_v2)) {
+ param(ID::MEMID);
+ param_bool(ID::CLK_ENABLE);
+ param_bool(ID::CLK_POLARITY);
+ param(ID::TRANSPARENCY_MASK);
+ param(ID::COLLISION_X_MASK);
+ param_bool(ID::CE_OVER_SRST);
+ param_bits(ID::ARST_VALUE, param(ID::WIDTH));
+ param_bits(ID::SRST_VALUE, param(ID::WIDTH));
+ param_bits(ID::INIT_VALUE, param(ID::WIDTH));
+ port(ID::CLK, 1);
+ port(ID::EN, 1);
+ port(ID::ARST, 1);
+ port(ID::SRST, 1);
+ port(ID::ADDR, param(ID::ABITS));
+ port(ID::DATA, param(ID::WIDTH));
+ check_expected();
+ return;
+ }
+
if (cell->type == ID($memwr)) {
param(ID::MEMID);
param_bool(ID::CLK_ENABLE);
@@ -1379,6 +1526,20 @@ namespace {
return;
}
+ if (cell->type == ID($memwr_v2)) {
+ param(ID::MEMID);
+ param_bool(ID::CLK_ENABLE);
+ param_bool(ID::CLK_POLARITY);
+ param(ID::PORTID);
+ param(ID::PRIORITY_MASK);
+ port(ID::CLK, 1);
+ port(ID::EN, param(ID::WIDTH));
+ port(ID::ADDR, param(ID::ABITS));
+ port(ID::DATA, param(ID::WIDTH));
+ check_expected();
+ return;
+ }
+
if (cell->type == ID($meminit)) {
param(ID::MEMID);
param(ID::PRIORITY);
@@ -1388,6 +1549,16 @@ namespace {
return;
}
+ if (cell->type == ID($meminit_v2)) {
+ param(ID::MEMID);
+ param(ID::PRIORITY);
+ port(ID::ADDR, param(ID::ABITS));
+ port(ID::DATA, param(ID::WIDTH) * param(ID::WORDS));
+ port(ID::EN, param(ID::WIDTH));
+ check_expected();
+ return;
+ }
+
if (cell->type == ID($mem)) {
param(ID::MEMID);
param(ID::SIZE);
@@ -1410,6 +1581,38 @@ namespace {
return;
}
+ if (cell->type == ID($mem_v2)) {
+ param(ID::MEMID);
+ param(ID::SIZE);
+ param(ID::OFFSET);
+ param(ID::INIT);
+ param_bits(ID::RD_CLK_ENABLE, max(1, param(ID::RD_PORTS)));
+ param_bits(ID::RD_CLK_POLARITY, max(1, param(ID::RD_PORTS)));
+ param_bits(ID::RD_TRANSPARENCY_MASK, max(1, param(ID::RD_PORTS) * param(ID::WR_PORTS)));
+ param_bits(ID::RD_COLLISION_X_MASK, max(1, param(ID::RD_PORTS) * param(ID::WR_PORTS)));
+ param_bits(ID::RD_WIDE_CONTINUATION, max(1, param(ID::RD_PORTS)));
+ param_bits(ID::RD_CE_OVER_SRST, max(1, param(ID::RD_PORTS)));
+ param_bits(ID::RD_ARST_VALUE, param(ID::RD_PORTS) * param(ID::WIDTH));
+ param_bits(ID::RD_SRST_VALUE, param(ID::RD_PORTS) * param(ID::WIDTH));
+ param_bits(ID::RD_INIT_VALUE, param(ID::RD_PORTS) * param(ID::WIDTH));
+ param_bits(ID::WR_CLK_ENABLE, max(1, param(ID::WR_PORTS)));
+ param_bits(ID::WR_CLK_POLARITY, max(1, param(ID::WR_PORTS)));
+ param_bits(ID::WR_WIDE_CONTINUATION, max(1, param(ID::WR_PORTS)));
+ param_bits(ID::WR_PRIORITY_MASK, max(1, param(ID::WR_PORTS) * param(ID::WR_PORTS)));
+ port(ID::RD_CLK, param(ID::RD_PORTS));
+ port(ID::RD_EN, param(ID::RD_PORTS));
+ port(ID::RD_ARST, param(ID::RD_PORTS));
+ port(ID::RD_SRST, param(ID::RD_PORTS));
+ port(ID::RD_ADDR, param(ID::RD_PORTS) * param(ID::ABITS));
+ port(ID::RD_DATA, param(ID::RD_PORTS) * param(ID::WIDTH));
+ port(ID::WR_CLK, param(ID::WR_PORTS));
+ port(ID::WR_EN, param(ID::WR_PORTS) * param(ID::WIDTH));
+ port(ID::WR_ADDR, param(ID::WR_PORTS) * param(ID::ABITS));
+ port(ID::WR_DATA, param(ID::WR_PORTS) * param(ID::WIDTH));
+ check_expected();
+ return;
+ }
+
if (cell->type == ID($tribuf)) {
port(ID::A, param(ID::WIDTH));
port(ID::Y, param(ID::WIDTH));
@@ -1536,6 +1739,15 @@ namespace {
{ port(ID::D,1); port(ID::Q,1); port(ID::C,1); port(ID::R,1); port(ID::E,1); check_expected(); return; }
if (cell->type.in(
+ ID($_ALDFF_NN_), ID($_ALDFF_NP_), ID($_ALDFF_PN_), ID($_ALDFF_PP_)))
+ { port(ID::D,1); port(ID::Q,1); port(ID::C,1); port(ID::L,1); port(ID::AD,1); check_expected(); return; }
+
+ if (cell->type.in(
+ ID($_ALDFFE_NNN_), ID($_ALDFFE_NNP_), ID($_ALDFFE_NPN_), ID($_ALDFFE_NPP_),
+ ID($_ALDFFE_PNN_), ID($_ALDFFE_PNP_), ID($_ALDFFE_PPN_), ID($_ALDFFE_PPP_)))
+ { port(ID::D,1); port(ID::Q,1); port(ID::C,1); port(ID::L,1); port(ID::AD,1); port(ID::E,1); check_expected(); return; }
+
+ 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_)))
{ port(ID::C,1); port(ID::S,1); port(ID::R,1); port(ID::D,1); port(ID::Q,1); check_expected(); return; }
@@ -1640,7 +1852,7 @@ void RTLIL::Module::check()
log_assert(!it.second->type.empty());
for (auto &it2 : it.second->connections()) {
log_assert(!it2.first.empty());
- it2.second.check();
+ it2.second.check(this);
}
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
@@ -1686,8 +1898,8 @@ void RTLIL::Module::check()
for (auto &it : connections_) {
log_assert(it.first.size() == it.second.size());
log_assert(!it.first.has_const());
- it.first.check();
- it.second.check();
+ it.first.check(this);
+ it.second.check(this);
}
for (auto &it : attributes)
@@ -1813,6 +2025,20 @@ void RTLIL::Module::add(RTLIL::Cell *cell)
cell->module = this;
}
+void RTLIL::Module::add(RTLIL::Process *process)
+{
+ log_assert(!process->name.empty());
+ log_assert(count_id(process->name) == 0);
+ processes[process->name] = process;
+ process->module = this;
+}
+
+void RTLIL::Module::add(RTLIL::Binding *binding)
+{
+ log_assert(binding != nullptr);
+ bindings_.push_back(binding);
+}
+
void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires)
{
log_assert(refcount_wires_ == 0);
@@ -1869,6 +2095,13 @@ void RTLIL::Module::remove(RTLIL::Cell *cell)
delete cell;
}
+void RTLIL::Module::remove(RTLIL::Process *process)
+{
+ log_assert(processes.count(process->name) != 0);
+ processes.erase(process->name);
+ delete process;
+}
+
void RTLIL::Module::rename(RTLIL::Wire *wire, RTLIL::IdString new_name)
{
log_assert(wires_[wire->name] == wire);
@@ -2094,11 +2327,19 @@ RTLIL::Memory *RTLIL::Module::addMemory(RTLIL::IdString name, const RTLIL::Memor
return mem;
}
+RTLIL::Process *RTLIL::Module::addProcess(RTLIL::IdString name)
+{
+ RTLIL::Process *proc = new RTLIL::Process;
+ proc->name = name;
+ add(proc);
+ return proc;
+}
+
RTLIL::Process *RTLIL::Module::addProcess(RTLIL::IdString name, const RTLIL::Process *other)
{
RTLIL::Process *proc = other->clone();
proc->name = name;
- processes[name] = proc;
+ add(proc);
return proc;
}
@@ -2219,6 +2460,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); \
@@ -2533,6 +2794,40 @@ RTLIL::Cell* RTLIL::Module::addAdffe(RTLIL::IdString name, const RTLIL::SigSpec
return cell;
}
+RTLIL::Cell* RTLIL::Module::addAldff(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q,
+ const RTLIL::SigSpec &sig_ad, bool clk_polarity, bool aload_polarity, const std::string &src)
+{
+ RTLIL::Cell *cell = addCell(name, ID($aldff));
+ cell->parameters[ID::CLK_POLARITY] = clk_polarity;
+ cell->parameters[ID::ALOAD_POLARITY] = aload_polarity;
+ cell->parameters[ID::WIDTH] = sig_q.size();
+ cell->setPort(ID::CLK, sig_clk);
+ cell->setPort(ID::ALOAD, sig_aload);
+ cell->setPort(ID::D, sig_d);
+ cell->setPort(ID::AD, sig_ad);
+ cell->setPort(ID::Q, sig_q);
+ cell->set_src_attribute(src);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addAldffe(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q,
+ const RTLIL::SigSpec &sig_ad, bool clk_polarity, bool en_polarity, bool aload_polarity, const std::string &src)
+{
+ RTLIL::Cell *cell = addCell(name, ID($aldffe));
+ cell->parameters[ID::CLK_POLARITY] = clk_polarity;
+ cell->parameters[ID::EN_POLARITY] = en_polarity;
+ cell->parameters[ID::ALOAD_POLARITY] = aload_polarity;
+ cell->parameters[ID::WIDTH] = sig_q.size();
+ cell->setPort(ID::CLK, sig_clk);
+ cell->setPort(ID::EN, sig_en);
+ cell->setPort(ID::ALOAD, sig_aload);
+ cell->setPort(ID::D, sig_d);
+ cell->setPort(ID::AD, sig_ad);
+ cell->setPort(ID::Q, sig_q);
+ cell->set_src_attribute(src);
+ return cell;
+}
+
RTLIL::Cell* RTLIL::Module::addSdff(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q,
RTLIL::Const srst_value, bool clk_polarity, bool srst_polarity, const std::string &src)
{
@@ -2723,6 +3018,33 @@ RTLIL::Cell* RTLIL::Module::addAdffeGate(RTLIL::IdString name, const RTLIL::SigS
return cell;
}
+RTLIL::Cell* RTLIL::Module::addAldffGate(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q,
+ const RTLIL::SigSpec &sig_ad, bool clk_polarity, bool aload_polarity, const std::string &src)
+{
+ RTLIL::Cell *cell = addCell(name, stringf("$_ALDFF_%c%c_", clk_polarity ? 'P' : 'N', aload_polarity ? 'P' : 'N'));
+ cell->setPort(ID::C, sig_clk);
+ cell->setPort(ID::L, sig_aload);
+ cell->setPort(ID::D, sig_d);
+ cell->setPort(ID::AD, sig_ad);
+ cell->setPort(ID::Q, sig_q);
+ cell->set_src_attribute(src);
+ return cell;
+}
+
+RTLIL::Cell* RTLIL::Module::addAldffeGate(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q,
+ const RTLIL::SigSpec &sig_ad, bool clk_polarity, bool en_polarity, bool aload_polarity, const std::string &src)
+{
+ RTLIL::Cell *cell = addCell(name, stringf("$_ALDFFE_%c%c%c_", clk_polarity ? 'P' : 'N', aload_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N'));
+ cell->setPort(ID::C, sig_clk);
+ cell->setPort(ID::L, sig_aload);
+ cell->setPort(ID::E, sig_en);
+ cell->setPort(ID::D, sig_d);
+ cell->setPort(ID::AD, sig_ad);
+ cell->setPort(ID::Q, sig_q);
+ cell->set_src_attribute(src);
+ return cell;
+}
+
RTLIL::Cell* RTLIL::Module::addSdffGate(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q,
bool srst_value, bool clk_polarity, bool srst_polarity, const std::string &src)
{
@@ -2894,6 +3216,13 @@ RTLIL::Memory::Memory()
#endif
}
+RTLIL::Process::Process() : module(nullptr)
+{
+ static unsigned int hashidx_count = 123456789;
+ hashidx_count = mkhash_xorshift(hashidx_count);
+ hashidx_ = hashidx_count;
+}
+
RTLIL::Cell::Cell() : module(nullptr)
{
static unsigned int hashidx_count = 123456789;
@@ -3065,14 +3394,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;
@@ -3119,6 +3455,16 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed)
check();
}
+bool RTLIL::Cell::has_memid() const
+{
+ return type.in(ID($memwr), ID($memwr_v2), ID($memrd), ID($memrd_v2), ID($meminit), ID($meminit_v2));
+}
+
+bool RTLIL::Cell::is_mem_cell() const
+{
+ return type.in(ID($mem), ID($mem_v2)) || has_memid();
+}
+
RTLIL::SigChunk::SigChunk()
{
wire = NULL;
@@ -3275,8 +3621,12 @@ RTLIL::SigSpec::SigSpec(const RTLIL::Const &value)
{
cover("kernel.rtlil.sigspec.init.const");
- chunks_.emplace_back(value);
- width_ = chunks_.back().width;
+ if (GetSize(value) != 0) {
+ chunks_.emplace_back(value);
+ width_ = chunks_.back().width;
+ } else {
+ width_ = 0;
+ }
hash_ = 0;
check();
}
@@ -3285,8 +3635,12 @@ RTLIL::SigSpec::SigSpec(const RTLIL::SigChunk &chunk)
{
cover("kernel.rtlil.sigspec.init.chunk");
- chunks_.emplace_back(chunk);
- width_ = chunks_.back().width;
+ if (chunk.width != 0) {
+ chunks_.emplace_back(chunk);
+ width_ = chunks_.back().width;
+ } else {
+ width_ = 0;
+ }
hash_ = 0;
check();
}
@@ -3295,8 +3649,12 @@ RTLIL::SigSpec::SigSpec(RTLIL::Wire *wire)
{
cover("kernel.rtlil.sigspec.init.wire");
- chunks_.emplace_back(wire);
- width_ = chunks_.back().width;
+ if (wire->width != 0) {
+ chunks_.emplace_back(wire);
+ width_ = chunks_.back().width;
+ } else {
+ width_ = 0;
+ }
hash_ = 0;
check();
}
@@ -3305,8 +3663,12 @@ RTLIL::SigSpec::SigSpec(RTLIL::Wire *wire, int offset, int width)
{
cover("kernel.rtlil.sigspec.init.wire_part");
- chunks_.emplace_back(wire, offset, width);
- width_ = chunks_.back().width;
+ if (width != 0) {
+ chunks_.emplace_back(wire, offset, width);
+ width_ = chunks_.back().width;
+ } else {
+ width_ = 0;
+ }
hash_ = 0;
check();
}
@@ -3315,8 +3677,12 @@ RTLIL::SigSpec::SigSpec(const std::string &str)
{
cover("kernel.rtlil.sigspec.init.str");
- chunks_.emplace_back(str);
- width_ = chunks_.back().width;
+ if (str.size() != 0) {
+ chunks_.emplace_back(str);
+ width_ = chunks_.back().width;
+ } else {
+ width_ = 0;
+ }
hash_ = 0;
check();
}
@@ -3325,7 +3691,8 @@ RTLIL::SigSpec::SigSpec(int val, int width)
{
cover("kernel.rtlil.sigspec.init.int");
- chunks_.emplace_back(val, width);
+ if (width != 0)
+ chunks_.emplace_back(val, width);
width_ = width;
hash_ = 0;
check();
@@ -3335,7 +3702,8 @@ RTLIL::SigSpec::SigSpec(RTLIL::State bit, int width)
{
cover("kernel.rtlil.sigspec.init.state");
- chunks_.emplace_back(bit, width);
+ if (width != 0)
+ chunks_.emplace_back(bit, width);
width_ = width;
hash_ = 0;
check();
@@ -3345,11 +3713,13 @@ RTLIL::SigSpec::SigSpec(const RTLIL::SigBit &bit, int width)
{
cover("kernel.rtlil.sigspec.init.bit");
- if (bit.wire == NULL)
- chunks_.emplace_back(bit.data, width);
- else
- for (int i = 0; i < width; i++)
- chunks_.push_back(bit);
+ if (width != 0) {
+ if (bit.wire == NULL)
+ chunks_.emplace_back(bit.data, width);
+ else
+ for (int i = 0; i < width; i++)
+ chunks_.push_back(bit);
+ }
width_ = width;
hash_ = 0;
check();
@@ -3794,7 +4164,13 @@ void RTLIL::SigSpec::remove_const()
width_ = 0;
for (auto &chunk : chunks_)
if (chunk.wire != NULL) {
- new_chunks.push_back(chunk);
+ if (!new_chunks.empty() &&
+ new_chunks.back().wire == chunk.wire &&
+ new_chunks.back().offset + new_chunks.back().width == chunk.offset) {
+ new_chunks.back().width += chunk.width;
+ } else {
+ new_chunks.push_back(chunk);
+ }
width_ += chunk.width;
}
@@ -3941,7 +4317,7 @@ RTLIL::SigSpec RTLIL::SigSpec::repeat(int num) const
}
#ifndef NDEBUG
-void RTLIL::SigSpec::check() const
+void RTLIL::SigSpec::check(Module *mod) const
{
if (width_ > 64)
{
@@ -3954,6 +4330,7 @@ void RTLIL::SigSpec::check() const
int w = 0;
for (size_t i = 0; i < chunks_.size(); i++) {
const RTLIL::SigChunk &chunk = chunks_[i];
+ log_assert(chunk.width != 0);
if (chunk.wire == NULL) {
if (i > 0)
log_assert(chunks_[i-1].wire != NULL);
@@ -3966,6 +4343,8 @@ void RTLIL::SigSpec::check() const
log_assert(chunk.width >= 0);
log_assert(chunk.offset + chunk.width <= chunk.wire->width);
log_assert(chunk.data.size() == 0);
+ if (mod != nullptr)
+ log_assert(chunk.wire->module == mod);
}
w += chunk.width;
}
@@ -3976,6 +4355,12 @@ void RTLIL::SigSpec::check() const
{
cover("kernel.rtlil.sigspec.check.unpacked");
+ if (mod != nullptr) {
+ for (size_t i = 0; i < bits_.size(); i++)
+ if (bits_[i].wire != nullptr)
+ log_assert(bits_[i].wire->module == mod);
+ }
+
log_assert(width_ == GetSize(bits_));
log_assert(chunks_.empty());
}
@@ -4164,6 +4549,19 @@ bool RTLIL::SigSpec::has_marked_bits() const
return false;
}
+bool RTLIL::SigSpec::is_onehot(int *pos) const
+{
+ cover("kernel.rtlil.sigspec.is_onehot");
+
+ pack();
+ if (!is_fully_const())
+ return false;
+ log_assert(GetSize(chunks_) <= 1);
+ if (width_)
+ return RTLIL::Const(chunks_[0].data).is_onehot(pos);
+ return false;
+}
+
bool RTLIL::SigSpec::as_bool() const
{
cover("kernel.rtlil.sigspec.as_bool");
@@ -4537,6 +4935,7 @@ RTLIL::SyncRule *RTLIL::SyncRule::clone() const
new_syncrule->type = type;
new_syncrule->signal = signal;
new_syncrule->actions = actions;
+ new_syncrule->mem_write_actions = mem_write_actions;
return new_syncrule;
}
diff --git a/kernel/rtlil.h b/kernel/rtlil.h
index 6170ea55e..d8300f159 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -1,7 +1,7 @@
/* -*- c++ -*-
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -69,8 +69,10 @@ namespace RTLIL
struct SigSpec;
struct CaseRule;
struct SwitchRule;
+ struct MemWriteAction;
struct SyncRule;
struct Process;
+ struct Binding;
typedef std::pair<SigSpec, SigSpec> SigSig;
@@ -165,7 +167,8 @@ namespace RTLIL
log_assert(p[0] == '$' || p[0] == '\\');
log_assert(p[1] != 0);
for (const char *c = p; *c; c++)
- log_assert((unsigned)*c > (unsigned)' ');
+ if ((unsigned)*c <= (unsigned)' ')
+ log_error("Found control character or space (0x%02hhx) in string '%s' which is not allowed in RTLIL identifiers\n", *c, p);
#ifndef YOSYS_NO_IDS_REFCNT
if (global_free_idx_list_.empty()) {
@@ -482,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.
@@ -660,6 +666,7 @@ struct RTLIL::Const
bool is_fully_ones() const;
bool is_fully_def() const;
bool is_fully_undef() const;
+ bool is_onehot(int *pos = nullptr) const;
inline RTLIL::Const extract(int offset, int len = 1, RTLIL::State padding = RTLIL::State::S0) const {
RTLIL::Const ret;
@@ -714,6 +721,9 @@ struct RTLIL::AttrObject
void set_hdlname_attribute(const vector<string> &hierarchy);
vector<string> get_hdlname_attribute() const;
+
+ void set_intvec_attribute(RTLIL::IdString id, const vector<int> &data);
+ vector<int> get_intvec_attribute(RTLIL::IdString id) const;
};
struct RTLIL::SigChunk
@@ -752,7 +762,7 @@ struct RTLIL::SigBit
SigBit();
SigBit(RTLIL::State bit);
- SigBit(bool bit);
+ explicit SigBit(bool bit);
SigBit(RTLIL::Wire *wire);
SigBit(RTLIL::Wire *wire, int offset);
SigBit(const RTLIL::SigChunk &chunk);
@@ -834,7 +844,7 @@ public:
SigSpec(const std::vector<RTLIL::SigBit> &bits);
SigSpec(const pool<RTLIL::SigBit> &bits);
SigSpec(const std::set<RTLIL::SigBit> &bits);
- SigSpec(bool bit);
+ explicit SigSpec(bool bit);
SigSpec(RTLIL::SigSpec &&other) {
width_ = other.width_;
@@ -932,6 +942,7 @@ public:
bool is_fully_undef() const;
bool has_const() const;
bool has_marked_bits() const;
+ bool is_onehot(int *pos = nullptr) const;
bool as_bool() const;
int as_int(bool is_signed = false) const;
@@ -960,9 +971,9 @@ public:
unsigned int hash() const { if (!hash_) updhash(); return hash_; };
#ifndef NDEBUG
- void check() const;
+ void check(Module *mod = nullptr) const;
#else
- void check() const { }
+ void check(Module *mod = nullptr) const { (void)mod; }
#endif
};
@@ -1029,6 +1040,8 @@ struct RTLIL::Design
int refcount_modules_;
dict<RTLIL::IdString, RTLIL::Module*> modules_;
+ std::vector<RTLIL::Binding*> bindings_;
+
std::vector<AST::AstNode*> verilog_packages, verilog_globals;
std::unique_ptr<define_map_t> verilog_defines;
@@ -1041,6 +1054,7 @@ struct RTLIL::Design
RTLIL::ObjRange<RTLIL::Module*> modules();
RTLIL::Module *module(RTLIL::IdString name);
+ const RTLIL::Module *module(RTLIL::IdString name) const;
RTLIL::Module *top_module();
bool has(RTLIL::IdString id) const {
@@ -1048,6 +1062,8 @@ struct RTLIL::Design
}
void add(RTLIL::Module *module);
+ void add(RTLIL::Binding *binding);
+
RTLIL::Module *addModule(RTLIL::IdString name);
void remove(RTLIL::Module *module);
void rename(RTLIL::Module *module, RTLIL::IdString new_name);
@@ -1110,7 +1126,7 @@ struct RTLIL::Design
std::vector<RTLIL::Module*> selected_modules() const;
std::vector<RTLIL::Module*> selected_whole_modules() const;
- std::vector<RTLIL::Module*> selected_whole_modules_warn() const;
+ std::vector<RTLIL::Module*> selected_whole_modules_warn(bool include_wb = false) const;
#ifdef WITH_PYTHON
static std::map<unsigned int, RTLIL::Design*> *get_all_designs(void);
#endif
@@ -1124,6 +1140,7 @@ struct RTLIL::Module : public RTLIL::AttrObject
protected:
void add(RTLIL::Wire *wire);
void add(RTLIL::Cell *cell);
+ void add(RTLIL::Process *process);
public:
RTLIL::Design *design;
@@ -1134,7 +1151,9 @@ public:
dict<RTLIL::IdString, RTLIL::Wire*> wires_;
dict<RTLIL::IdString, RTLIL::Cell*> cells_;
- std::vector<RTLIL::SigSig> connections_;
+
+ std::vector<RTLIL::SigSig> connections_;
+ std::vector<RTLIL::Binding*> bindings_;
RTLIL::IdString name;
idict<RTLIL::IdString> avail_parameters;
@@ -1147,7 +1166,8 @@ public:
virtual RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, bool mayfail = false);
virtual RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, const dict<RTLIL::IdString, RTLIL::Module*> &interfaces, const dict<RTLIL::IdString, RTLIL::IdString> &modports, bool mayfail = false);
virtual size_t count_id(RTLIL::IdString id);
- virtual void reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces);
+ virtual void expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces);
+ virtual bool reprocess_if_necessary(RTLIL::Design *design);
virtual void sort();
virtual void check();
@@ -1189,12 +1209,24 @@ public:
return it == cells_.end() ? nullptr : it->second;
}
+ const RTLIL::Wire* wire(RTLIL::IdString id) const{
+ auto it = wires_.find(id);
+ return it == wires_.end() ? nullptr : it->second;
+ }
+ const RTLIL::Cell* cell(RTLIL::IdString id) const {
+ auto it = cells_.find(id);
+ return it == cells_.end() ? nullptr : it->second;
+ }
+
RTLIL::ObjRange<RTLIL::Wire*> wires() { return RTLIL::ObjRange<RTLIL::Wire*>(&wires_, &refcount_wires_); }
RTLIL::ObjRange<RTLIL::Cell*> cells() { return RTLIL::ObjRange<RTLIL::Cell*>(&cells_, &refcount_cells_); }
+ void add(RTLIL::Binding *binding);
+
// Removing wires is expensive. If you have to remove wires, remove them all at once.
void remove(const pool<RTLIL::Wire*> &wires);
void remove(RTLIL::Cell *cell);
+ void remove(RTLIL::Process *process);
void rename(RTLIL::Wire *wire, RTLIL::IdString new_name);
void rename(RTLIL::Cell *cell, RTLIL::IdString new_name);
@@ -1214,6 +1246,7 @@ public:
RTLIL::Memory *addMemory(RTLIL::IdString name, const RTLIL::Memory *other);
+ RTLIL::Process *addProcess(RTLIL::IdString name);
RTLIL::Process *addProcess(RTLIL::IdString name, const RTLIL::Process *other);
// The add* methods create a cell and return the created cell. All signals must exist in advance.
@@ -1266,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 = "");
@@ -1286,6 +1321,8 @@ public:
RTLIL::Cell* addDffsre (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_set, const RTLIL::SigSpec &sig_clr, RTLIL::SigSpec sig_d, const RTLIL::SigSpec &sig_q, bool clk_polarity = true, bool en_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = "");
RTLIL::Cell* addAdff (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_arst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const arst_value, bool clk_polarity = true, bool arst_polarity = true, const std::string &src = "");
RTLIL::Cell* addAdffe (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_arst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const arst_value, bool clk_polarity = true, bool en_polarity = true, bool arst_polarity = true, const std::string &src = "");
+ RTLIL::Cell* addAldff (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, const RTLIL::SigSpec &sig_ad, bool clk_polarity = true, bool aload_polarity = true, const std::string &src = "");
+ RTLIL::Cell* addAldffe (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, const RTLIL::SigSpec &sig_ad, bool clk_polarity = true, bool en_polarity = true, bool aload_polarity = true, const std::string &src = "");
RTLIL::Cell* addSdff (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const srst_value, bool clk_polarity = true, bool srst_polarity = true, const std::string &src = "");
RTLIL::Cell* addSdffe (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const srst_value, bool clk_polarity = true, bool en_polarity = true, bool srst_polarity = true, const std::string &src = "");
RTLIL::Cell* addSdffce (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const srst_value, bool clk_polarity = true, bool en_polarity = true, bool srst_polarity = true, const std::string &src = "");
@@ -1323,6 +1360,10 @@ public:
bool arst_value = false, bool clk_polarity = true, bool arst_polarity = true, const std::string &src = "");
RTLIL::Cell* addAdffeGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_arst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q,
bool arst_value = false, bool clk_polarity = true, bool en_polarity = true, bool arst_polarity = true, const std::string &src = "");
+ RTLIL::Cell* addAldffGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q,
+ const RTLIL::SigSpec &sig_ad, bool clk_polarity = true, bool aload_polarity = true, const std::string &src = "");
+ RTLIL::Cell* addAldffeGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q,
+ const RTLIL::SigSpec &sig_ad, bool clk_polarity = true, bool en_polarity = true, bool aload_polarity = true, const std::string &src = "");
RTLIL::Cell* addSdffGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q,
bool srst_value = false, bool clk_polarity = true, bool srst_polarity = true, const std::string &src = "");
RTLIL::Cell* addSdffeGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q,
@@ -1339,7 +1380,6 @@ public:
RTLIL::SigSpec Not (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec Pos (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
- RTLIL::SigSpec Bu0 (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec Neg (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec And (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
@@ -1386,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 = "");
@@ -1511,6 +1553,9 @@ public:
#ifdef WITH_PYTHON
static std::map<unsigned int, RTLIL::Cell*> *get_all_cells(void);
#endif
+
+ bool has_memid() const;
+ bool is_mem_cell() const;
};
struct RTLIL::CaseRule : public RTLIL::AttrObject
@@ -1520,7 +1565,6 @@ struct RTLIL::CaseRule : public RTLIL::AttrObject
std::vector<RTLIL::SwitchRule*> switches;
~CaseRule();
- void optimize();
bool empty() const;
@@ -1543,11 +1587,21 @@ struct RTLIL::SwitchRule : public RTLIL::AttrObject
RTLIL::SwitchRule *clone() const;
};
+struct RTLIL::MemWriteAction : RTLIL::AttrObject
+{
+ RTLIL::IdString memid;
+ RTLIL::SigSpec address;
+ RTLIL::SigSpec data;
+ RTLIL::SigSpec enable;
+ RTLIL::Const priority_mask;
+};
+
struct RTLIL::SyncRule
{
RTLIL::SyncType type;
RTLIL::SigSpec signal;
std::vector<RTLIL::SigSig> actions;
+ std::vector<RTLIL::MemWriteAction> mem_write_actions;
template<typename T> void rewrite_sigspecs(T &functor);
template<typename T> void rewrite_sigspecs2(T &functor);
@@ -1556,12 +1610,21 @@ struct RTLIL::SyncRule
struct RTLIL::Process : public RTLIL::AttrObject
{
+ unsigned int hashidx_;
+ unsigned int hash() const { return hashidx_; }
+
+protected:
+ // use module->addProcess() and module->remove() to create or destroy processes
+ friend struct RTLIL::Module;
+ Process();
+ ~Process();
+
+public:
RTLIL::IdString name;
+ RTLIL::Module *module;
RTLIL::CaseRule root_case;
std::vector<RTLIL::SyncRule*> syncs;
- ~Process();
-
template<typename T> void rewrite_sigspecs(T &functor);
template<typename T> void rewrite_sigspecs2(T &functor);
RTLIL::Process *clone() const;
@@ -1695,6 +1758,11 @@ void RTLIL::SyncRule::rewrite_sigspecs(T &functor)
functor(it.first);
functor(it.second);
}
+ for (auto &it : mem_write_actions) {
+ functor(it.address);
+ functor(it.data);
+ functor(it.enable);
+ }
}
template<typename T>
@@ -1704,6 +1772,11 @@ void RTLIL::SyncRule::rewrite_sigspecs2(T &functor)
for (auto &it : actions) {
functor(it.first, it.second);
}
+ for (auto &it : mem_write_actions) {
+ functor(it.address);
+ functor(it.data);
+ functor(it.enable);
+ }
}
template<typename T>
diff --git a/kernel/satgen.cc b/kernel/satgen.cc
index 2a54e78ec..9c40ec66d 100644
--- a/kernel/satgen.cc
+++ b/kernel/satgen.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -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);
@@ -1081,7 +1181,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
FfData ff(nullptr, cell);
// Latches and FFs with async inputs are not supported — use clk2fflogic or async2sync first.
- if (!ff.has_d || ff.has_arst || ff.has_sr || (ff.has_en && !ff.has_clk))
+ if (ff.has_aload || ff.has_arst || ff.has_sr)
return false;
if (timestep == 1)
@@ -1094,7 +1194,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
std::vector<int> undef_d;
if (model_undef)
undef_d = importUndefSigSpec(cell->getPort(ID::D), timestep-1);
- if (ff.has_srst && ff.has_en && ff.ce_over_srst) {
+ if (ff.has_srst && ff.has_ce && ff.ce_over_srst) {
int srst = importDefSigSpec(ff.sig_srst, timestep-1).at(0);
std::vector<int> rval = importDefSigSpec(ff.val_srst, timestep-1);
int undef_srst;
@@ -1108,21 +1208,21 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
else
std::tie(d, undef_d) = mux(srst, undef_srst, rval, undef_rval, d, undef_d);
}
- if (ff.has_en) {
- int en = importDefSigSpec(ff.sig_en, timestep-1).at(0);
+ if (ff.has_ce) {
+ int ce = importDefSigSpec(ff.sig_ce, timestep-1).at(0);
std::vector<int> old_q = importDefSigSpec(ff.sig_q, timestep-1);
- int undef_en;
+ int undef_ce;
std::vector<int> undef_old_q;
if (model_undef) {
- undef_en = importUndefSigSpec(ff.sig_en, timestep-1).at(0);
+ undef_ce = importUndefSigSpec(ff.sig_ce, timestep-1).at(0);
undef_old_q = importUndefSigSpec(ff.sig_q, timestep-1);
}
- if (ff.pol_en)
- std::tie(d, undef_d) = mux(en, undef_en, old_q, undef_old_q, d, undef_d);
+ if (ff.pol_ce)
+ std::tie(d, undef_d) = mux(ce, undef_ce, old_q, undef_old_q, d, undef_d);
else
- std::tie(d, undef_d) = mux(en, undef_en, d, undef_d, old_q, undef_old_q);
+ std::tie(d, undef_d) = mux(ce, undef_ce, d, undef_d, old_q, undef_old_q);
}
- if (ff.has_srst && !(ff.has_en && ff.ce_over_srst)) {
+ if (ff.has_srst && !(ff.has_ce && ff.ce_over_srst)) {
int srst = importDefSigSpec(ff.sig_srst, timestep-1).at(0);
std::vector<int> rval = importDefSigSpec(ff.val_srst, timestep-1);
int undef_srst;
diff --git a/kernel/satgen.h b/kernel/satgen.h
index cf2db733f..da2cec222 100644
--- a/kernel/satgen.h
+++ b/kernel/satgen.h
@@ -1,7 +1,7 @@
/* -*- c++ -*-
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/kernel/sigtools.h b/kernel/sigtools.h
index c631fa481..4ea43d743 100644
--- a/kernel/sigtools.h
+++ b/kernel/sigtools.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/kernel/timinginfo.h b/kernel/timinginfo.h
index eba3386d6..e7e4eab6e 100644
--- a/kernel/timinginfo.h
+++ b/kernel/timinginfo.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* (C) 2020 Eddie Hung <eddie@fpgeh.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
@@ -49,9 +49,9 @@ struct TimingInfo
struct ModuleTiming
{
- RTLIL::IdString type;
dict<BitBit, int> comb;
- dict<NameBit, int> arrival, required;
+ dict<NameBit, std::pair<int,NameBit>> arrival, required;
+ bool has_inputs;
};
dict<RTLIL::IdString, ModuleTiming> data;
@@ -120,11 +120,10 @@ struct TimingInfo
}
}
else if (cell->type == ID($specify3)) {
- auto src = cell->getPort(ID::SRC);
+ auto src = cell->getPort(ID::SRC).as_bit();
auto dst = cell->getPort(ID::DST);
- for (const auto &c : src.chunks())
- if (!c.wire->port_input)
- log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src));
+ if (!src.wire || !src.wire->port_input)
+ log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src));
for (const auto &c : dst.chunks())
if (!c.wire->port_output)
log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module output.\n", log_id(module), log_id(cell), log_signal(dst));
@@ -136,34 +135,49 @@ struct TimingInfo
max = 0;
}
for (const auto &d : dst) {
- auto &v = t.arrival[NameBit(d)];
- v = std::max(v, max);
+ auto r = t.arrival.insert(NameBit(d));
+ auto &v = r.first->second;
+ if (r.second || v.first < max) {
+ v.first = max;
+ v.second = NameBit(src);
+ }
}
}
else if (cell->type == ID($specrule)) {
- auto type = cell->getParam(ID::TYPE).decode_string();
- if (type != "$setup" && type != "$setuphold")
+ IdString type = cell->getParam(ID::TYPE).decode_string();
+ if (type != ID($setup) && type != ID($setuphold))
continue;
auto src = cell->getPort(ID::SRC);
- auto dst = cell->getPort(ID::DST);
+ auto dst = cell->getPort(ID::DST).as_bit();
for (const auto &c : src.chunks())
- if (!c.wire->port_input)
+ if (!c.wire || !c.wire->port_input)
log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src));
- for (const auto &c : dst.chunks())
- if (!c.wire->port_input)
- log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(dst));
+ if (!dst.wire || !dst.wire->port_input)
+ log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(dst));
int max = cell->getParam(ID::T_LIMIT_MAX).as_int();
if (max < 0) {
log_warning("Module '%s' contains specify cell '%s' with T_LIMIT_MAX < 0 which is currently unsupported. Clamping to 0.\n", log_id(module), log_id(cell));
max = 0;
}
for (const auto &s : src) {
- auto &v = t.required[NameBit(s)];
- v = std::max(v, max);
+ auto r = t.required.insert(NameBit(s));
+ auto &v = r.first->second;
+ if (r.second || v.first < max) {
+ v.first = max;
+ v.second = NameBit(dst);
+ }
}
}
}
+ for (auto port_name : module->ports) {
+ auto wire = module->wire(port_name);
+ if (wire->port_input) {
+ t.has_inputs = true;
+ break;
+ }
+ }
+
return t;
}
diff --git a/kernel/utils.h b/kernel/utils.h
index 8942905fe..d37f045ff 100644
--- a/kernel/utils.h
+++ b/kernel/utils.h
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/kernel/yosys.cc b/kernel/yosys.cc
index 1caf80c11..09909696b 100644
--- a/kernel/yosys.cc
+++ b/kernel/yosys.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -67,6 +67,7 @@
# define INIT_MODULE initlibyosys
extern "C" void INIT_MODULE();
#endif
+#include <signal.h>
#endif
#include <limits.h>
@@ -137,7 +138,7 @@ void yosys_banner()
log(" | |\n");
log(" | yosys -- Yosys Open SYnthesis Suite |\n");
log(" | |\n");
- log(" | Copyright (C) 2012 - 2020 Claire Wolf <claire@symbioticeda.com> |\n");
+ log(" | Copyright (C) 2012 - 2020 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | |\n");
log(" | Permission to use, copy, modify, and/or distribute this software for any |\n");
log(" | purpose with or without fee is hereby granted, provided that the above |\n");
@@ -540,6 +541,7 @@ void yosys_setup()
PyImport_AppendInittab((char*)"libyosys", INIT_MODULE);
Py_Initialize();
PyRun_SimpleString("import sys");
+ signal(SIGINT, SIG_DFL);
#endif
Pass::init_register();
@@ -814,7 +816,9 @@ std::string proc_self_dirname()
path = (char *) realloc((void *) path, buflen);
while (buflen > 0 && path[buflen-1] != '/')
buflen--;
- return std::string(path, buflen);
+ std::string str(path, buflen);
+ free(path);
+ return str;
}
#elif defined(_WIN32)
std::string proc_self_dirname()
@@ -969,7 +973,7 @@ static void handle_label(std::string &command, bool &from_to_active, const std::
}
}
-void run_frontend(std::string filename, std::string command, std::string *backend_command, std::string *from_to_label, RTLIL::Design *design)
+bool run_frontend(std::string filename, std::string command, RTLIL::Design *design, std::string *from_to_label)
{
if (design == nullptr)
design = yosys_design;
@@ -979,11 +983,11 @@ void run_frontend(std::string filename, std::string command, std::string *backen
if (filename_trim.size() > 3 && filename_trim.compare(filename_trim.size()-3, std::string::npos, ".gz") == 0)
filename_trim.erase(filename_trim.size()-3);
if (filename_trim.size() > 2 && filename_trim.compare(filename_trim.size()-2, std::string::npos, ".v") == 0)
- command = "verilog";
+ command = " -vlog2k";
else if (filename_trim.size() > 2 && filename_trim.compare(filename_trim.size()-3, std::string::npos, ".sv") == 0)
- command = "verilog -sv";
+ command = " -sv";
else if (filename_trim.size() > 3 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".vhd") == 0)
- command = "vhdl";
+ command = " -vhdl";
else if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-5, std::string::npos, ".blif") == 0)
command = "blif";
else if (filename_trim.size() > 5 && filename_trim.compare(filename_trim.size()-6, std::string::npos, ".eblif") == 0)
@@ -1069,10 +1073,12 @@ void run_frontend(std::string filename, std::string command, std::string *backen
if (filename != "-")
fclose(f);
- if (backend_command != NULL && *backend_command == "auto")
- *backend_command = "";
+ return true;
+ }
- return;
+ if (command == "tcl") {
+ Pass::call(design, vector<string>({command, filename}));
+ return true;
}
if (filename == "-") {
@@ -1081,16 +1087,15 @@ void run_frontend(std::string filename, std::string command, std::string *backen
log("\n-- Parsing `%s' using frontend `%s' --\n", filename.c_str(), command.c_str());
}
- if (command == "tcl")
- Pass::call(design, vector<string>({command, filename}));
- else
+ if (command[0] == ' ') {
+ auto argv = split_tokens("read" + command);
+ argv.push_back(filename);
+ Pass::call(design, argv);
+ } else
Frontend::frontend_call(design, NULL, filename, command);
- design->check();
-}
-void run_frontend(std::string filename, std::string command, RTLIL::Design *design)
-{
- run_frontend(filename, command, nullptr, nullptr, design);
+ design->check();
+ return false;
}
void run_pass(std::string command, RTLIL::Design *design)
@@ -1404,7 +1409,7 @@ struct ScriptCmdPass : public Pass {
else if (args.size() == 2)
run_frontend(args[1], "script", design);
else if (args.size() == 3)
- run_frontend(args[1], "script", NULL, &args[2], design);
+ run_frontend(args[1], "script", design, &args[2]);
else
extra_args(args, 2, design, false);
}
diff --git a/kernel/yosys.h b/kernel/yosys.h
index 5df7e2df0..93e7ff23e 100644
--- a/kernel/yosys.h
+++ b/kernel/yosys.h
@@ -1,7 +1,7 @@
/* -*- c++ -*-
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -33,7 +33,7 @@
// This header is very boring. It just defines some general things that
// belong nowhere else and includes the interesting headers.
//
-// Find more information in the "CodingReadme" file.
+// Find more information in the "guidelines/GettingStarted" file.
#ifndef YOSYS_H
@@ -147,9 +147,7 @@ extern Tcl_Obj *Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *p
# define YS_ATTRIBUTE(...)
#endif
-#if __cplusplus >= 201703L
-# define YS_MAYBE_UNUSED [[maybe_unused]];
-#elif defined(__GNUC__) || defined(__clang__)
+#if defined(__GNUC__) || defined(__clang__)
# define YS_MAYBE_UNUSED __attribute__((__unused__))
#else
# define YS_MAYBE_UNUSED
@@ -222,6 +220,7 @@ namespace RTLIL {
struct Wire;
struct Cell;
struct Memory;
+ struct Process;
struct Module;
struct Design;
struct Monitor;
@@ -245,6 +244,7 @@ namespace hashlib {
template<> struct hash_ops<RTLIL::Wire*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Cell*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Memory*> : hash_obj_ops {};
+ template<> struct hash_ops<RTLIL::Process*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Module*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Design*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Monitor*> : hash_obj_ops {};
@@ -253,6 +253,7 @@ namespace hashlib {
template<> struct hash_ops<const RTLIL::Wire*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Cell*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Memory*> : hash_obj_ops {};
+ template<> struct hash_ops<const RTLIL::Process*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Module*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Design*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Monitor*> : hash_obj_ops {};
@@ -349,8 +350,7 @@ std::vector<std::string> glob_filename(const std::string &filename_pattern);
void rewrite_filename(std::string &filename);
void run_pass(std::string command, RTLIL::Design *design = nullptr);
-void run_frontend(std::string filename, std::string command, std::string *backend_command, std::string *from_to_label = nullptr, RTLIL::Design *design = nullptr);
-void run_frontend(std::string filename, std::string command, RTLIL::Design *design = nullptr);
+bool run_frontend(std::string filename, std::string command, RTLIL::Design *design = nullptr, std::string *from_to_label = nullptr);
void run_backend(std::string filename, std::string command, RTLIL::Design *design = nullptr);
void shell(RTLIL::Design *design);