aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG1
-rw-r--r--passes/memory/memory_dff.cc16
-rw-r--r--passes/opt/muxpack.cc368
-rw-r--r--techlibs/xilinx/cells_sim.v17
-rw-r--r--techlibs/xilinx/cells_xtra.sh2
-rw-r--r--techlibs/xilinx/cells_xtra.v7
-rw-r--r--techlibs/xilinx/drams.txt20
-rw-r--r--techlibs/xilinx/drams_map.v34
-rw-r--r--tests/memories/issue00335.v28
-rw-r--r--tests/memories/issue00710.v17
-rwxr-xr-xtests/memories/run-test.sh6
-rw-r--r--tests/various/muxpack.v259
-rw-r--r--tests/various/muxpack.ys268
13 files changed, 1029 insertions, 14 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 0636e6bad..f0154a81e 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -21,6 +21,7 @@ Yosys 0.8 .. Yosys 0.8-dev
- Added "muxcover -mux{4,8,16}=<cost>"
- Added "muxcover -dmux=<cost>"
- Added "muxcover -nopartial"
+ - Added "muxpack" pass
- Added "abc9" pass for timing-aware techmapping (experimental, FPGA only, no FFs)
- Added "synth_xilinx -abc9" (experimental)
- Added "synth_ice40 -abc9" (experimental)
diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc
index 220d29295..5215cce44 100644
--- a/passes/memory/memory_dff.cc
+++ b/passes/memory/memory_dff.cc
@@ -182,11 +182,17 @@ struct MemoryDffWorker
if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data))
{
- bool enable_invert = mux_cells_a.count(sig_data) != 0;
- Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data);
- SigSpec check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A"));
+ RTLIL::SigSpec en;
+ RTLIL::SigSpec check_q;
+
+ do {
+ bool enable_invert = mux_cells_a.count(sig_data) != 0;
+ Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data);
+ check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A"));
+ sig_data = sigmap(mux->getPort("\\Y"));
+ en.append(enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S"));
+ } while (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data));
- sig_data = sigmap(mux->getPort("\\Y"));
for (auto bit : sig_data)
if (sigbit_users_count[bit] > 1)
goto skip_ff_after_read_merging;
@@ -195,7 +201,7 @@ struct MemoryDffWorker
{
disconnect_dff(sig_data);
cell->setPort("\\CLK", clk_data);
- cell->setPort("\\EN", enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S"));
+ cell->setPort("\\EN", en.size() > 1 ? module->ReduceAnd(NEW_ID, en) : en);
cell->setPort("\\DATA", sig_data);
cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
diff --git a/passes/opt/muxpack.cc b/passes/opt/muxpack.cc
new file mode 100644
index 000000000..6697d6ca1
--- /dev/null
+++ b/passes/opt/muxpack.cc
@@ -0,0 +1,368 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 2019 Eddie Hung <eddie@fpgeh.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/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct ExclusiveDatabase
+{
+ Module *module;
+ const SigMap &sigmap;
+
+ dict<SigBit, std::pair<SigSpec,std::vector<Const>>> sig_cmp_prev;
+
+ ExclusiveDatabase(Module *module, const SigMap &sigmap) : module(module), sigmap(sigmap)
+ {
+ SigSpec const_sig, nonconst_sig;
+ SigBit y_port;
+ pool<Cell*> reduce_or;
+ for (auto cell : module->cells()) {
+ if (cell->type == "$eq") {
+ nonconst_sig = sigmap(cell->getPort("\\A"));
+ const_sig = sigmap(cell->getPort("\\B"));
+ if (!const_sig.is_fully_const()) {
+ if (!nonconst_sig.is_fully_const())
+ continue;
+ std::swap(nonconst_sig, const_sig);
+ }
+ y_port = sigmap(cell->getPort("\\Y"));
+ }
+ else if (cell->type == "$logic_not") {
+ nonconst_sig = sigmap(cell->getPort("\\A"));
+ const_sig = Const(RTLIL::S0, GetSize(nonconst_sig));
+ y_port = sigmap(cell->getPort("\\Y"));
+ }
+ else if (cell->type == "$reduce_or") {
+ reduce_or.insert(cell);
+ continue;
+ }
+ else continue;
+
+ log_assert(!nonconst_sig.empty());
+ log_assert(!const_sig.empty());
+ sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,std::vector<Const>{const_sig.as_const()});
+ }
+
+ for (auto cell : reduce_or) {
+ nonconst_sig = SigSpec();
+ std::vector<Const> values;
+ SigSpec a_port = sigmap(cell->getPort("\\A"));
+ for (auto bit : a_port) {
+ auto it = sig_cmp_prev.find(bit);
+ if (it == sig_cmp_prev.end()) {
+ nonconst_sig = SigSpec();
+ break;
+ }
+ if (nonconst_sig.empty())
+ nonconst_sig = it->second.first;
+ else if (nonconst_sig != it->second.first) {
+ nonconst_sig = SigSpec();
+ break;
+ }
+ for (auto value : it->second.second)
+ values.push_back(value);
+ }
+ if (nonconst_sig.empty())
+ continue;
+ y_port = sigmap(cell->getPort("\\Y"));
+ sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,std::move(values));
+ }
+ }
+
+ bool query(const SigSpec &sig) const
+ {
+ SigSpec nonconst_sig;
+ pool<Const> const_values;
+
+ for (auto bit : sig.bits()) {
+ auto it = sig_cmp_prev.find(bit);
+ if (it == sig_cmp_prev.end())
+ return false;
+
+ if (nonconst_sig.empty())
+ nonconst_sig = it->second.first;
+ else if (nonconst_sig != it->second.first)
+ return false;
+
+ for (auto value : it->second.second)
+ if (!const_values.insert(value).second)
+ return false;
+ }
+
+ return true;
+ }
+};
+
+
+struct MuxpackWorker
+{
+ Module *module;
+ SigMap sigmap;
+
+ int mux_count, pmux_count;
+
+ pool<Cell*> remove_cells;
+
+ dict<SigSpec, Cell*> sig_chain_next;
+ dict<SigSpec, Cell*> sig_chain_prev;
+ pool<SigBit> sigbit_with_non_chain_users;
+ pool<Cell*> chain_start_cells;
+ pool<Cell*> candidate_cells;
+
+ ExclusiveDatabase excl_db;
+
+ void make_sig_chain_next_prev()
+ {
+ for (auto wire : module->wires())
+ {
+ if (wire->port_output || wire->get_bool_attribute("\\keep")) {
+ for (auto bit : sigmap(wire))
+ sigbit_with_non_chain_users.insert(bit);
+ }
+ }
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type.in("$mux", "$pmux") && !cell->get_bool_attribute("\\keep"))
+ {
+ SigSpec a_sig = sigmap(cell->getPort("\\A"));
+ SigSpec b_sig;
+ if (cell->type == "$mux")
+ b_sig = sigmap(cell->getPort("\\B"));
+ SigSpec y_sig = sigmap(cell->getPort("\\Y"));
+
+ if (sig_chain_next.count(a_sig))
+ for (auto a_bit : a_sig.bits())
+ sigbit_with_non_chain_users.insert(a_bit);
+ else {
+ sig_chain_next[a_sig] = cell;
+ candidate_cells.insert(cell);
+ }
+
+ if (!b_sig.empty()) {
+ if (sig_chain_next.count(b_sig))
+ for (auto b_bit : b_sig.bits())
+ sigbit_with_non_chain_users.insert(b_bit);
+ else {
+ sig_chain_next[b_sig] = cell;
+ candidate_cells.insert(cell);
+ }
+ }
+
+ sig_chain_prev[y_sig] = cell;
+ continue;
+ }
+
+ for (auto conn : cell->connections())
+ if (cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ sigbit_with_non_chain_users.insert(bit);
+ }
+ }
+
+ void find_chain_start_cells()
+ {
+ for (auto cell : candidate_cells)
+ {
+ log_debug("Considering %s (%s)\n", log_id(cell), log_id(cell->type));
+
+ SigSpec a_sig = sigmap(cell->getPort("\\A"));
+ if (cell->type == "$mux") {
+ SigSpec b_sig = sigmap(cell->getPort("\\B"));
+ if (sig_chain_prev.count(a_sig) + sig_chain_prev.count(b_sig) != 1)
+ goto start_cell;
+
+ if (!sig_chain_prev.count(a_sig))
+ a_sig = b_sig;
+ }
+ else if (cell->type == "$pmux") {
+ if (!sig_chain_prev.count(a_sig))
+ goto start_cell;
+ }
+ else log_abort();
+
+ for (auto bit : a_sig.bits())
+ if (sigbit_with_non_chain_users.count(bit))
+ goto start_cell;
+
+ {
+ Cell *prev_cell = sig_chain_prev.at(a_sig);
+ log_assert(prev_cell);
+ SigSpec s_sig = sigmap(cell->getPort("\\S"));
+ s_sig.append(sigmap(prev_cell->getPort("\\S")));
+ if (!excl_db.query(s_sig))
+ goto start_cell;
+ }
+
+ continue;
+
+ start_cell:
+ chain_start_cells.insert(cell);
+ }
+ }
+
+ vector<Cell*> create_chain(Cell *start_cell)
+ {
+ vector<Cell*> chain;
+
+ Cell *c = start_cell;
+ while (c != nullptr)
+ {
+ chain.push_back(c);
+
+ SigSpec y_sig = sigmap(c->getPort("\\Y"));
+
+ if (sig_chain_next.count(y_sig) == 0)
+ break;
+
+ c = sig_chain_next.at(y_sig);
+ if (chain_start_cells.count(c) != 0)
+ break;
+ }
+
+ return chain;
+ }
+
+ void process_chain(vector<Cell*> &chain)
+ {
+ if (GetSize(chain) < 2)
+ return;
+
+ int cursor = 0;
+ while (cursor < GetSize(chain))
+ {
+ int cases = GetSize(chain) - cursor;
+
+ Cell *first_cell = chain[cursor];
+ dict<int, SigBit> taps_dict;
+
+ if (cases < 2) {
+ cursor++;
+ continue;
+ }
+
+ Cell *last_cell = chain[cursor+cases-1];
+
+ log("Converting %s.%s ... %s.%s to a pmux with %d cases.\n",
+ log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), cases);
+
+ mux_count += cases;
+ pmux_count += 1;
+
+ first_cell->type = "$pmux";
+ SigSpec b_sig = first_cell->getPort("\\B");
+ SigSpec s_sig = first_cell->getPort("\\S");
+
+ for (int i = 1; i < cases; i++) {
+ Cell* prev_cell = chain[cursor+i-1];
+ Cell* cursor_cell = chain[cursor+i];
+ if (sigmap(prev_cell->getPort("\\Y")) == sigmap(cursor_cell->getPort("\\A"))) {
+ b_sig.append(cursor_cell->getPort("\\B"));
+ s_sig.append(cursor_cell->getPort("\\S"));
+ }
+ else {
+ log_assert(cursor_cell->type == "$mux");
+ b_sig.append(cursor_cell->getPort("\\A"));
+ s_sig.append(module->LogicNot(NEW_ID, cursor_cell->getPort("\\S")));
+ }
+ remove_cells.insert(cursor_cell);
+ }
+
+ first_cell->setPort("\\B", b_sig);
+ first_cell->setPort("\\S", s_sig);
+ first_cell->setParam("\\S_WIDTH", GetSize(s_sig));
+ first_cell->setPort("\\Y", last_cell->getPort("\\Y"));
+
+ cursor += cases;
+ }
+ }
+
+ void cleanup()
+ {
+ for (auto cell : remove_cells)
+ module->remove(cell);
+
+ remove_cells.clear();
+ sig_chain_next.clear();
+ sig_chain_prev.clear();
+ chain_start_cells.clear();
+ candidate_cells.clear();
+ }
+
+ MuxpackWorker(Module *module) :
+ module(module), sigmap(module), mux_count(0), pmux_count(0), excl_db(module, sigmap)
+ {
+ make_sig_chain_next_prev();
+ find_chain_start_cells();
+
+ for (auto c : chain_start_cells) {
+ vector<Cell*> chain = create_chain(c);
+ process_chain(chain);
+ }
+
+ cleanup();
+ }
+};
+
+struct MuxpackPass : public Pass {
+ MuxpackPass() : Pass("muxpack", "$mux/$pmux cascades to $pmux") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" muxpack [selection]\n");
+ log("\n");
+ log("This pass converts cascaded chains of $pmux cells (e.g. those create from case\n");
+ log("constructs) and $mux cells (e.g. those created by if-else constructs) into\n");
+ log("$pmux cells.\n");
+ log("\n");
+ log("This optimisation is conservative --- it will only pack $mux or $pmux cells\n");
+ log("whose select lines are driven by '$eq' cells with other such cells if it can be\n");
+ log("certain that their select inputs are mutually exclusive.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing MUXPACK pass ($mux cell cascades to $pmux).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ int mux_count = 0;
+ int pmux_count = 0;
+
+ for (auto module : design->selected_modules()) {
+ MuxpackWorker worker(module);
+ mux_count += worker.mux_count;
+ pmux_count += worker.pmux_count;
+ }
+
+ log("Converted %d (p)mux cells into %d pmux cells.\n", mux_count, pmux_count);
+ }
+} MuxpackPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v
index 8261286af..67b221c95 100644
--- a/techlibs/xilinx/cells_sim.v
+++ b/techlibs/xilinx/cells_sim.v
@@ -281,6 +281,23 @@ module FDPE_1 (output reg Q, input C, CE, D, PRE);
always @(negedge C, posedge PRE) if (PRE) Q <= 1'b1; else if (CE) Q <= D;
endmodule
+module RAM32X1D (
+ output DPO, SPO,
+ input D, WCLK, WE,
+ input A0, A1, A2, A3, A4,
+ input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4,
+);
+ parameter INIT = 32'h0;
+ parameter IS_WCLK_INVERTED = 1'b0;
+ wire [4:0] a = {A4, A3, A2, A1, A0};
+ wire [4:0] dpra = {DPRA4, DPRA3, DPRA2, DPRA1, DPRA0};
+ reg [31:0] mem = INIT;
+ assign SPO = mem[a];
+ assign DPO = mem[dpra];
+ wire clk = WCLK ^ IS_WCLK_INVERTED;
+ always @(posedge clk) if (WE) mem[a] <= D;
+endmodule
+
(* abc_box_id = 4, abc_scc_break="D" *)
module RAM64X1D (
output DPO, SPO,
diff --git a/techlibs/xilinx/cells_xtra.sh b/techlibs/xilinx/cells_xtra.sh
index 2b384f405..53b528820 100644
--- a/techlibs/xilinx/cells_xtra.sh
+++ b/techlibs/xilinx/cells_xtra.sh
@@ -120,7 +120,7 @@ function xtract_cell_decl()
xtract_cell_decl RAM128X1S
xtract_cell_decl RAM256X1S
xtract_cell_decl RAM32M
- xtract_cell_decl RAM32X1D
+ #xtract_cell_decl RAM32X1D
xtract_cell_decl RAM32X1S
xtract_cell_decl RAM32X1S_1
xtract_cell_decl RAM32X2S
diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v
index 0ec3d0df0..15fa1b63a 100644
--- a/techlibs/xilinx/cells_xtra.v
+++ b/techlibs/xilinx/cells_xtra.v
@@ -3694,13 +3694,6 @@ module RAM32M (...);
input WE;
endmodule
-module RAM32X1D (...);
- parameter [31:0] INIT = 32'h00000000;
- parameter [0:0] IS_WCLK_INVERTED = 1'b0;
- output DPO, SPO;
- input A0, A1, A2, A3, A4, D, DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, WCLK, WE;
-endmodule
-
module RAM32X1S (...);
parameter [31:0] INIT = 32'h00000000;
parameter [0:0] IS_WCLK_INVERTED = 1'b0;
diff --git a/techlibs/xilinx/drams.txt b/techlibs/xilinx/drams.txt
index 91632bcee..2613c206c 100644
--- a/techlibs/xilinx/drams.txt
+++ b/techlibs/xilinx/drams.txt
@@ -1,4 +1,17 @@
+bram $__XILINX_RAM32X1D
+ init 1
+ abits 5
+ dbits 1
+ groups 2
+ ports 1 1
+ wrmode 0 1
+ enable 0 1
+ transp 0 0
+ clocks 0 1
+ clkpol 0 2
+endbram
+
bram $__XILINX_RAM64X1D
init 1
abits 6
@@ -25,6 +38,13 @@ bram $__XILINX_RAM128X1D
clkpol 0 2
endbram
+match $__XILINX_RAM32X1D
+ min bits 3
+ min wports 1
+ make_outreg
+ or_next_if_better
+endmatch
+
match $__XILINX_RAM64X1D
min bits 5
min wports 1
diff --git a/techlibs/xilinx/drams_map.v b/techlibs/xilinx/drams_map.v
index 47476b592..77041ca86 100644
--- a/techlibs/xilinx/drams_map.v
+++ b/techlibs/xilinx/drams_map.v
@@ -1,4 +1,38 @@
+module \$__XILINX_RAM32X1D (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN);
+ parameter [31:0] INIT = 32'bx;
+ parameter CLKPOL2 = 1;
+ input CLK1;
+
+ input [4:0] A1ADDR;
+ output A1DATA;
+
+ input [4:0] B1ADDR;
+ input B1DATA;
+ input B1EN;
+
+ RAM32X1D #(
+ .INIT(INIT),
+ .IS_WCLK_INVERTED(!CLKPOL2)
+ ) _TECHMAP_REPLACE_ (
+ .DPRA0(A1ADDR[0]),
+ .DPRA1(A1ADDR[1]),
+ .DPRA2(A1ADDR[2]),
+ .DPRA3(A1ADDR[3]),
+ .DPRA4(A1ADDR[4]),
+ .DPO(A1DATA),
+
+ .A0(B1ADDR[0]),
+ .A1(B1ADDR[1]),
+ .A2(B1ADDR[2]),
+ .A3(B1ADDR[3]),
+ .A4(B1ADDR[4]),
+ .D(B1DATA),
+ .WCLK(CLK1),
+ .WE(B1EN)
+ );
+endmodule
+
module \$__XILINX_RAM64X1D (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN);
parameter [63:0] INIT = 64'bx;
parameter CLKPOL2 = 1;
diff --git a/tests/memories/issue00335.v b/tests/memories/issue00335.v
new file mode 100644
index 000000000..f3b6e5dfe
--- /dev/null
+++ b/tests/memories/issue00335.v
@@ -0,0 +1,28 @@
+// expect-wr-ports 1
+// expect-rd-ports 1
+// expect-rd-clk \clk
+
+module ram2 (input clk,
+ input sel,
+ input we,
+ input [SIZE-1:0] adr,
+ input [63:0] dat_i,
+ output reg [63:0] dat_o);
+ parameter SIZE = 5; // Address size
+
+ reg [63:0] mem [0:(1 << SIZE)-1];
+ integer i;
+
+ initial begin
+ for (i = 0; i < (1<<SIZE) - 1; i = i + 1)
+ mem[i] <= 0;
+ end
+
+ always @(posedge clk)
+ if (sel) begin
+ if (~we)
+ dat_o <= mem[adr];
+ else
+ mem[adr] <= dat_i;
+ end
+endmodule
diff --git a/tests/memories/issue00710.v b/tests/memories/issue00710.v
new file mode 100644
index 000000000..7a5fad1c2
--- /dev/null
+++ b/tests/memories/issue00710.v
@@ -0,0 +1,17 @@
+// expect-wr-ports 1
+// expect-rd-ports 1
+// expect-rd-clk \clk
+
+module top(input clk, input we, re, reset, input [7:0] addr, wdata, output reg [7:0] rdata);
+
+reg [7:0] bram[0:255];
+(* keep *) reg dummy;
+
+always @(posedge clk)
+ if (reset)
+ dummy <= 1'b0;
+ else if (re)
+ rdata <= bram[addr];
+ else if (we)
+ bram[addr] <= wdata;
+endmodule
diff --git a/tests/memories/run-test.sh b/tests/memories/run-test.sh
index 734a96682..d0537bb98 100755
--- a/tests/memories/run-test.sh
+++ b/tests/memories/run-test.sh
@@ -14,7 +14,7 @@ shift "$((OPTIND-1))"
bash ../tools/autotest.sh $seed -G *.v
-for f in `egrep -l 'expect-(wr|rd)-ports' *.v`; do
+for f in `egrep -l 'expect-(wr-ports|rd-ports|rd-clk)' *.v`; do
echo -n "Testing expectations for $f .."
../../yosys -qp "proc; opt; memory -nomap;; dump -outfile ${f%.v}.dmp t:\$mem" $f
if grep -q expect-wr-ports $f; then
@@ -25,6 +25,10 @@ for f in `egrep -l 'expect-(wr|rd)-ports' *.v`; do
grep -q "parameter \\\\RD_PORTS $(gawk '/expect-rd-ports/ { print $3; }' $f)\$" ${f%.v}.dmp ||
{ echo " ERROR: Unexpected number of read ports."; false; }
fi
+ if grep -q expect-rd-clk $f; then
+ grep -q "connect \\\\RD_CLK \\$(gawk '/expect-rd-clk/ { print $3; }' $f)\$" ${f%.v}.dmp ||
+ { echo " ERROR: Unexpected read clock."; false; }
+ fi
echo " ok."
done
diff --git a/tests/various/muxpack.v b/tests/various/muxpack.v
new file mode 100644
index 000000000..33ece1f16
--- /dev/null
+++ b/tests/various/muxpack.v
@@ -0,0 +1,259 @@
+module mux_if_unbal_4_1 #(parameter N=4, parameter W=1) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o);
+always @*
+ if (s == 0) o <= i[0*W+:W];
+ else if (s == 1) o <= i[1*W+:W];
+ else if (s == 2) o <= i[2*W+:W];
+ else if (s == 3) o <= i[3*W+:W];
+ else o <= {W{1'bx}};
+endmodule
+
+module mux_if_unbal_5_3 #(parameter N=5, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o);
+always @* begin
+ o <= {W{1'bx}};
+ if (s == 0) o <= i[0*W+:W];
+ if (s == 1) o <= i[1*W+:W];
+ if (s == 2) o <= i[2*W+:W];
+ if (s == 3) o <= i[3*W+:W];
+ if (s == 4) o <= i[4*W+:W];
+end
+endmodule
+
+module mux_if_unbal_5_3_invert #(parameter N=5, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o);
+always @*
+ if (s != 0)
+ if (s != 1)
+ if (s != 2)
+ if (s != 3)
+ if (s != 4) o <= i[4*W+:W];
+ else o <= i[0*W+:W];
+ else o <= i[3*W+:W];
+ else o <= i[2*W+:W];
+ else o <= i[1*W+:W];
+ else o <= {W{1'bx}};
+endmodule
+
+module mux_if_unbal_5_3_width_mismatch #(parameter N=5, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o);
+always @* begin
+ o <= {W{1'bx}};
+ if (s == 0) o <= i[0*W+:W];
+ if (s == 1) o <= i[1*W+:W];
+ if (s == 2) o[W-2:0] <= i[2*W+:W-1];
+ if (s == 3) o <= i[3*W+:W];
+ if (s == 4) o <= i[4*W+:W];
+end
+endmodule
+
+module mux_if_unbal_4_1_missing #(parameter N=5, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o);
+always @* begin
+ if (s == 0) o <= i[0*W+:W];
+// else if (s == 1) o <= i[1*W+:W];
+// else if (s == 2) o <= i[2*W+:W];
+ else if (s == 3) o <= i[3*W+:W];
+ else o <= {W{1'bx}};
+end
+endmodule
+
+module mux_if_unbal_5_3_order #(parameter N=5, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o);
+always @* begin
+ o <= {W{1'bx}};
+ if (s == 3) o <= i[3*W+:W];
+ if (s == 2) o <= i[2*W+:W];
+ if (s == 1) o <= i[1*W+:W];
+ if (s == 4) o <= i[4*W+:W];
+ if (s == 0) o <= i[0*W+:W];
+end
+endmodule
+
+module mux_if_unbal_4_1_nonexcl #(parameter N=4, parameter W=1) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o);
+always @*
+ if (s == 0) o <= i[0*W+:W];
+ else if (s == 1) o <= i[1*W+:W];
+ else if (s == 2) o <= i[2*W+:W];
+ else if (s == 3) o <= i[3*W+:W];
+ else if (s == 0) o <= {W{1'b0}};
+ else o <= {W{1'bx}};
+endmodule
+
+module mux_if_unbal_5_3_nonexcl #(parameter N=5, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o);
+always @* begin
+ o <= {W{1'bx}};
+ if (s == 0) o <= i[0*W+:W];
+ if (s == 1) o <= i[1*W+:W];
+ if (s == 2) o <= i[2*W+:W];
+ if (s == 3) o <= i[3*W+:W];
+ if (s == 4) o <= i[4*W+:W];
+ if (s == 0) o <= i[2*W+:W];
+end
+endmodule
+
+module mux_case_unbal_8_7#(parameter N=8, parameter W=7) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o);
+always @* begin
+ o <= {W{1'bx}};
+ case (s)
+ 0: o <= i[0*W+:W];
+ default:
+ case (s)
+ 1: o <= i[1*W+:W];
+ 2: o <= i[2*W+:W];
+ default:
+ case (s)
+ 3: o <= i[3*W+:W];
+ 4: o <= i[4*W+:W];
+ 5: o <= i[5*W+:W];
+ default:
+ case (s)
+ 6: o <= i[6*W+:W];
+ default: o <= i[7*W+:W];
+ endcase
+ endcase
+ endcase
+ endcase
+end
+endmodule
+
+module mux_if_bal_8_2 #(parameter N=8, parameter W=2) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o);
+always @*
+ if (s[0] == 1'b0)
+ if (s[1] == 1'b0)
+ if (s[2] == 1'b0)
+ o <= i[0*W+:W];
+ else
+ o <= i[1*W+:W];
+ else
+ if (s[2] == 1'b0)
+ o <= i[2*W+:W];
+ else
+ o <= i[3*W+:W];
+ else
+ if (s[1] == 1'b0)
+ if (s[2] == 1'b0)
+ o <= i[4*W+:W];
+ else
+ o <= i[5*W+:W];
+ else
+ if (s[2] == 1'b0)
+ o <= i[6*W+:W];
+ else
+ o <= i[7*W+:W];
+endmodule
+
+module mux_if_bal_5_1 #(parameter N=5, parameter W=1) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output reg [W-1:0] o);
+always @*
+ if (s[0] == 1'b0)
+ if (s[1] == 1'b0)
+ if (s[2] == 1'b0)
+ o <= i[0*W+:W];
+ else
+ o <= i[1*W+:W];
+ else
+ if (s[2] == 1'b0)
+ o <= i[2*W+:W];
+ else
+ o <= i[3*W+:W];
+ else
+ o <= i[4*W+:W];
+endmodule
+
+module cliffordwolf_nonexclusive_select (
+ input wire x, y, z,
+ input wire a, b, c, d,
+ output reg o
+);
+ always @* begin
+ o = a;
+ if (x) o = b;
+ if (y) o = c;
+ if (z) o = d;
+ end
+endmodule
+
+module cliffordwolf_freduce (
+ input wire [1:0] s,
+ input wire a, b, c, d,
+ output reg [3:0] o
+);
+ always @* begin
+ o = {4{a}};
+ if (s == 0) o = {3{b}};
+ if (s == 1) o = {2{c}};
+ if (s == 2) o = d;
+ end
+endmodule
+
+module case_nonexclusive_select (
+ input wire [1:0] x, y,
+ input wire a, b, c, d, e,
+ output reg o
+);
+ always @* begin
+ case (x)
+ 0: o = b;
+ 2: o = b;
+ 1: o = c;
+ default: begin
+ o = a;
+ if (y == 0) o = d;
+ if (y == 1) o = e;
+ end
+ endcase
+ end
+endmodule
+
+module case_nonoverlap (
+ input wire [2:0] x,
+ input wire a, b, c, d, e,
+ output reg o
+);
+ always @* begin
+ case (x)
+ 0, 2: o = b; // Creates $reduce_or
+ 1: o = c;
+ default:
+ case (x)
+ 3: o = d; 4: o = d; // Creates $reduce_or
+ 5: o = e;
+ default: o = 1'b0;
+ endcase
+ endcase
+ end
+endmodule
+
+module case_overlap (
+ input wire [2:0] x,
+ input wire a, b, c, d, e,
+ output reg o
+);
+ always @* begin
+ case (x)
+ 0, 2: o = b; // Creates $reduce_or
+ 1: o = c;
+ default:
+ case (x)
+ 0: o = 1'b1; // OVERLAP!
+ 3, 4: o = d; // Creates $reduce_or
+ 5: o = e;
+ default: o = 1'b0;
+ endcase
+ endcase
+ end
+endmodule
+
+module case_overlap2 (
+ input wire [2:0] x,
+ input wire a, b, c, d, e,
+ output reg o
+);
+ always @* begin
+ case (x)
+ 0: o = b; 2: o = b; // Creates $reduce_or
+ 1: o = c;
+ default:
+ case (x)
+ 0: o = d; 2: o = d; // Creates $reduce_or
+ 3: o = d; 4: o = d; // Creates $reduce_or
+ 5: o = e;
+ default: o = 1'b0;
+ endcase
+ endcase
+ end
+endmodule
diff --git a/tests/various/muxpack.ys b/tests/various/muxpack.ys
new file mode 100644
index 000000000..af23fcec8
--- /dev/null
+++ b/tests/various/muxpack.ys
@@ -0,0 +1,268 @@
+read_verilog muxpack.v
+design -save read
+
+hierarchy -top mux_if_unbal_4_1
+prep
+design -save gold
+muxpack
+opt
+stat
+select -assert-count 0 t:$mux
+select -assert-count 1 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+design -load read
+hierarchy -top mux_if_unbal_5_3
+prep
+design -save gold
+muxpack
+opt
+stat
+select -assert-count 0 t:$mux
+select -assert-count 1 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+# TODO: Currently ExclusiveDatabase only analyses $eq cells
+#design -load read
+#hierarchy -top mux_if_unbal_5_3_invert
+#prep
+#design -save gold
+#muxpack
+#opt
+#stat
+#select -assert-count 0 t:$mux
+#select -assert-count 1 t:$pmux
+#design -stash gate
+#design -import gold -as gold
+#design -import gate -as gate
+#miter -equiv -flatten -make_assert -make_outputs gold gate miter
+#sat -verify -prove-asserts -show-ports miter
+
+design -load read
+hierarchy -top mux_if_unbal_5_3_width_mismatch
+prep
+design -save gold
+muxpack
+opt
+stat
+select -assert-count 0 t:$mux
+select -assert-count 2 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+design -load read
+hierarchy -top mux_if_unbal_4_1_missing
+prep
+design -save gold
+muxpack
+opt
+stat
+select -assert-count 0 t:$mux
+select -assert-count 1 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+design -load read
+hierarchy -top mux_if_unbal_5_3_order
+prep
+design -save gold
+muxpack
+opt
+stat
+select -assert-count 0 t:$mux
+select -assert-count 1 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+design -load read
+hierarchy -top mux_if_unbal_4_1_nonexcl
+prep
+design -save gold
+muxpack
+opt
+stat
+select -assert-count 0 t:$mux
+select -assert-count 1 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+design -load read
+hierarchy -top mux_if_unbal_5_3_nonexcl
+prep
+design -save gold
+muxpack
+opt
+stat
+select -assert-count 0 t:$mux
+select -assert-count 1 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+design -load read
+hierarchy -top mux_case_unbal_8_7
+prep
+design -save gold
+muxpack
+opt
+stat
+select -assert-count 0 t:$mux
+select -assert-count 1 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+design -load read
+hierarchy -top mux_if_bal_8_2
+prep
+design -save gold
+muxpack
+opt
+stat
+select -assert-count 7 t:$mux
+select -assert-count 0 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+design -load read
+hierarchy -top mux_if_bal_5_1
+prep
+design -save gold
+muxpack
+opt
+stat
+select -assert-count 4 t:$mux
+select -assert-count 0 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+design -load read
+hierarchy -top cliffordwolf_nonexclusive_select
+prep
+design -save gold
+muxpack
+opt
+stat
+select -assert-count 3 t:$mux
+select -assert-count 0 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+#design -load read
+#hierarchy -top cliffordwolf_freduce
+#prep
+#design -save gold
+#proc; opt; freduce; opt
+#show
+#muxpack
+#opt
+#stat
+#select -assert-count 0 t:$mux
+#select -assert-count 1 t:$pmux
+#design -stash gate
+#design -import gold -as gold
+#design -import gate -as gate
+#miter -equiv -flatten -make_assert -make_outputs gold gate miter
+#sat -verify -prove-asserts -show-ports miter
+
+design -load read
+hierarchy -top case_nonexclusive_select
+prep
+design -save gold
+muxpack
+opt
+stat
+select -assert-count 0 t:$mux
+select -assert-count 2 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+design -load read
+hierarchy -top case_nonoverlap
+#prep # Do not prep otherwise $pmux's overlapping entry will get removed
+proc
+design -save gold
+opt -fast -mux_undef
+select -assert-count 2 t:$pmux
+muxpack
+opt
+stat
+select -assert-count 0 t:$mux
+select -assert-count 1 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+design -load read
+hierarchy -top case_overlap
+#prep # Do not prep otherwise $pmux's overlapping entry will get removed
+proc
+design -save gold
+opt -fast -mux_undef
+select -assert-count 2 t:$pmux
+muxpack
+opt
+stat
+select -assert-count 0 t:$mux
+select -assert-count 2 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+design -load read
+hierarchy -top case_overlap2
+#prep # Do not prep otherwise $pmux's overlapping entry will get removed
+proc
+design -save gold
+opt -fast -mux_undef
+select -assert-count 2 t:$pmux
+muxpack
+opt
+stat
+select -assert-count 0 t:$mux
+select -assert-count 2 t:$pmux
+design -stash gate
+design -import gold -as gold
+design -import gate -as gate
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter