aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG2
-rw-r--r--frontends/aiger/aigerparse.cc3
-rw-r--r--kernel/rtlil.cc13
-rw-r--r--kernel/rtlil.h1
-rw-r--r--passes/opt/Makefile.inc1
-rw-r--r--passes/opt/muxpack.cc337
-rw-r--r--passes/techmap/abc9.cc13
-rw-r--r--techlibs/xilinx/Makefile.inc1
-rw-r--r--techlibs/xilinx/abc_xc7.box11
-rw-r--r--techlibs/xilinx/cells_map.v162
-rw-r--r--techlibs/xilinx/cells_sim.v25
-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--techlibs/xilinx/mux_map.v71
-rw-r--r--techlibs/xilinx/synth_xilinx.cc108
-rw-r--r--tests/various/muxpack.v199
-rw-r--r--tests/various/muxpack.ys214
19 files changed, 1166 insertions, 58 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 0636e6bad..abfbb7f79 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -21,11 +21,13 @@ 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)
- Added "synth -abc9" (experimental)
- "synth_xilinx" to now infer hard shift registers (-nosrl to disable)
+ - "synth_xilinx" to now infer wide multiplexers (-nomux to disable)
- Fixed sign extension of unsized constants with 'bx and 'bz MSB
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc
index efd265464..43337f4c2 100644
--- a/frontends/aiger/aigerparse.cc
+++ b/frontends/aiger/aigerparse.cc
@@ -22,9 +22,6 @@
// Armin Biere. The AIGER And-Inverter Graph (AIG) Format Version 20071012. Technical Report 07/1, October 2011, FMV Reports Series, Institute for Formal Models and Verification, Johannes Kepler University, Altenbergerstr. 69, 4040 Linz, Austria.
// http://fmv.jku.at/papers/Biere-FMV-TR-07-1.pdf
-#ifdef _WIN32
-#include <libgen.h>
-#endif
// https://stackoverflow.com/a/46137633
#ifdef _MSC_VER
#include <stdlib.h>
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index 502b45cfd..94dbf31c0 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -1592,21 +1592,14 @@ void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires)
void RTLIL::Module::remove(RTLIL::Cell *cell)
{
- auto it = cells_.find(cell->name);
- log_assert(it != cells_.end());
- remove(it);
-}
-
-dict<RTLIL::IdString, RTLIL::Cell*>::iterator RTLIL::Module::remove(dict<RTLIL::IdString, RTLIL::Cell*>::iterator it)
-{
- RTLIL::Cell *cell = it->second;
while (!cell->connections_.empty())
cell->unsetPort(cell->connections_.begin()->first);
+ auto it = cells_.find(cell->name);
+ log_assert(it != cells_.end());
log_assert(refcount_cells_ == 0);
- it = cells_.erase(it);
+ cells_.erase(it);
delete cell;
- return it;
}
void RTLIL::Module::rename(RTLIL::Wire *wire, RTLIL::IdString new_name)
diff --git a/kernel/rtlil.h b/kernel/rtlil.h
index 4a0f8b4f8..f4fcf5dcf 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -1040,7 +1040,6 @@ public:
// 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);
- dict<RTLIL::IdString, RTLIL::Cell*>::iterator remove(dict<RTLIL::IdString, RTLIL::Cell*>::iterator it);
void rename(RTLIL::Wire *wire, RTLIL::IdString new_name);
void rename(RTLIL::Cell *cell, RTLIL::IdString new_name);
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
index 337fee9e4..ea3646330 100644
--- a/passes/opt/Makefile.inc
+++ b/passes/opt/Makefile.inc
@@ -14,5 +14,6 @@ OBJS += passes/opt/opt_demorgan.o
OBJS += passes/opt/rmports.o
OBJS += passes/opt/opt_lut.o
OBJS += passes/opt/pmux2shiftx.o
+OBJS += passes/opt/muxpack.o
endif
diff --git a/passes/opt/muxpack.cc b/passes/opt/muxpack.cc
new file mode 100644
index 000000000..b6f3313bf
--- /dev/null
+++ b/passes/opt/muxpack.cc
@@ -0,0 +1,337 @@
+/*
+ * 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, SigSpec> sig_cmp_prev;
+ dict<SigSpec, pool<SigSpec>> sig_exclusive;
+
+ ExclusiveDatabase(Module *module, const SigMap &sigmap) : module(module), sigmap(sigmap)
+ {
+ SigSpec a_port, b_port, y_port;
+ for (auto cell : module->cells()) {
+ if (cell->type == "$eq") {
+ a_port = sigmap(cell->getPort("\\A"));
+ b_port = sigmap(cell->getPort("\\B"));
+ if (!b_port.is_fully_const()) {
+ if (!a_port.is_fully_const())
+ continue;
+ std::swap(a_port, b_port);
+ }
+ y_port = sigmap(cell->getPort("\\Y"));
+ }
+ else if (cell->type == "$logic_not") {
+ a_port = sigmap(cell->getPort("\\A"));
+ b_port = Const(RTLIL::S0, GetSize(a_port));
+ y_port = sigmap(cell->getPort("\\Y"));
+ }
+ else continue;
+
+ auto r = sig_exclusive[a_port].insert(b_port.as_const());
+ if (!r.second)
+ continue;
+ sig_cmp_prev[y_port] = a_port;
+ }
+ }
+
+ bool query(const SigSpec& sig1, const SigSpec& sig2) const
+ {
+ // FIXME: O(N^2)
+ for (auto bit1 : sig1.bits()) {
+ auto it = sig_cmp_prev.find(bit1);
+ if (it == sig_cmp_prev.end())
+ return false;
+
+ for (auto bit2 : sig2.bits()) {
+ auto jt = sig_cmp_prev.find(bit2);
+ if (jt == sig_cmp_prev.end())
+ return false;
+
+ if (it->second != jt->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"));
+ SigSpec next_s_sig = sigmap(prev_cell->getPort("\\S"));
+ if (!excl_db.query(s_sig, next_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 with\n");
+ log("other such cells if it can be certain that the select lines are mutually\n");
+ log("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/passes/techmap/abc9.cc b/passes/techmap/abc9.cc
index cd7954427..f90834aa9 100644
--- a/passes/techmap/abc9.cc
+++ b/passes/techmap/abc9.cc
@@ -550,16 +550,15 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
}
vector<RTLIL::Cell*> boxes;
- for (auto it = module->cells_.begin(); it != module->cells_.end(); ) {
- RTLIL::Cell* cell = it->second;
+ for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it) {
+ RTLIL::Cell *cell = it->second;
if (cell->type.in("$_AND_", "$_NOT_", "$__ABC_FF_")) {
- it = module->remove(it);
+ module->remove(cell);
continue;
}
RTLIL::Module* box_module = design->module(cell->type);
if (box_module && box_module->attributes.count("\\abc_box_id"))
- boxes.emplace_back(it->second);
- ++it;
+ boxes.emplace_back(cell);
}
std::map<std::string, int> cell_stats;
@@ -665,8 +664,8 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
}
}
- for (auto cell : boxes)
- module->remove(cell);
+ for (auto cell : boxes)
+ module->remove(cell);
// Copy connections (and rename) from mapped_mod to module
for (auto conn : mapped_mod->connections()) {
diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc
index 1a652eb27..59fd61cf0 100644
--- a/techlibs/xilinx/Makefile.inc
+++ b/techlibs/xilinx/Makefile.inc
@@ -30,6 +30,7 @@ $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/drams_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/arith_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/ff_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lut_map.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/mux_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7.box))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_xc7.lut))
diff --git a/techlibs/xilinx/abc_xc7.box b/techlibs/xilinx/abc_xc7.box
index 40b92da0c..dafef9fef 100644
--- a/techlibs/xilinx/abc_xc7.box
+++ b/techlibs/xilinx/abc_xc7.box
@@ -3,17 +3,22 @@
# NB: Inputs/Outputs must be ordered alphabetically
# (with exceptions for carry in/out)
-# F7BMUX slower than F7AMUX
+# Average across F7[AB]MUX
# Inputs: I0 I1 S0
# Outputs: O
-F7BMUX 1 1 3 1
-217 223 296
+F7MUX 1 1 3 1
+204 208 286
# Inputs: I0 I1 S0
# Outputs: O
MUXF8 2 1 3 1
104 94 273
+# Inputs: I0 I1 I2 I3 S0 S1
+# Outputs: O
+MUXF78 10 1 6 1
+190 193 217 223 296 273
+
# CARRY4 + CARRY4_[ABCD]X
# Inputs: CYINIT DI0 DI1 DI2 DI3 S0 S1 S2 S3 CI
# Outputs: O0 O1 O2 O3 CO0 CO1 CO2 CO3
diff --git a/techlibs/xilinx/cells_map.v b/techlibs/xilinx/cells_map.v
index b5114758c..bc0455b0c 100644
--- a/techlibs/xilinx/cells_map.v
+++ b/techlibs/xilinx/cells_map.v
@@ -154,3 +154,165 @@ module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, o
end
endgenerate
endmodule
+
+`ifdef MIN_MUX_INPUTS
+module \$__XILINX_SHIFTX (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ parameter [A_WIDTH-1:0] _TECHMAP_CONSTMSK_A_ = 0;
+ parameter [A_WIDTH-1:0] _TECHMAP_CONSTVAL_A_ = 0;
+ parameter [B_WIDTH-1:0] _TECHMAP_CONSTMSK_B_ = 0;
+ parameter [B_WIDTH-1:0] _TECHMAP_CONSTVAL_B_ = 0;
+
+ function integer compute_num_leading_X_in_A;
+ integer i, c;
+ begin
+ compute_num_leading_X_in_A = 0;
+ c = 1;
+ for (i = A_WIDTH-1; i >= 0; i=i-1) begin
+ if (!_TECHMAP_CONSTMSK_A_[i] || _TECHMAP_CONSTVAL_A_[i] !== 1'bx)
+ c = 0;
+ compute_num_leading_X_in_A = compute_num_leading_X_in_A + c;
+ end
+ end
+ endfunction
+ localparam num_leading_X_in_A = compute_num_leading_X_in_A();
+
+ generate
+ genvar i, j;
+ // Bit-blast
+ if (Y_WIDTH > 1) begin
+ for (i = 0; i < Y_WIDTH; i++)
+ \$__XILINX_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(A_WIDTH-Y_WIDTH+1), .B_WIDTH(B_WIDTH), .Y_WIDTH(1'd1)) bitblast (.A(A[A_WIDTH-Y_WIDTH+i:i]), .B(B), .Y(Y[i]));
+ end
+ // If the LSB of B is constant zero (and Y_WIDTH is 1) then
+ // we can optimise by removing every other entry from A
+ // and popping the constant zero from B
+ else if (_TECHMAP_CONSTMSK_B_[0] && !_TECHMAP_CONSTVAL_B_[0]) begin
+ wire [(A_WIDTH+1)/2-1:0] A_i;
+ for (i = 0; i < (A_WIDTH+1)/2; i++)
+ assign A_i[i] = A[i*2];
+ \$__XILINX_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH((A_WIDTH+1'd1)/2'd2), .B_WIDTH(B_WIDTH-1'd1), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(A_i), .B(B[B_WIDTH-1:1]), .Y(Y));
+ end
+ // Trim off any leading 1'bx -es in A, and resize B accordingly
+ else if (num_leading_X_in_A > 0) begin
+ localparam A_WIDTH_new = A_WIDTH - num_leading_X_in_A;
+ localparam B_WIDTH_new = $clog2(A_WIDTH_new);
+ \$__XILINX_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(A_WIDTH_new), .B_WIDTH(B_WIDTH_new), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(A[A_WIDTH_new-1:0]), .B(B[B_WIDTH_new-1:0]), .Y(Y));
+ end
+ else if (A_WIDTH < `MIN_MUX_INPUTS) begin
+ wire _TECHMAP_FAIL_ = 1;
+ end
+ else if (A_WIDTH <= 2 ** 3) begin
+ localparam a_width0 = 2 ** 2;
+ localparam a_widthN = A_WIDTH - a_width0;
+ wire T0, T1;
+ \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_width0), .B_WIDTH(2), .Y_WIDTH(Y_WIDTH)) fpga_soft_mux (.A(A[a_width0-1:0]), .B(B[2-1:0]), .Y(T0));
+ if (a_widthN > 1)
+ \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_widthN), .B_WIDTH($clog2(a_widthN)), .Y_WIDTH(Y_WIDTH)) fpga_soft_mux_last (.A(A[A_WIDTH-1:a_width0]), .B(B[$clog2(a_widthN)-1:0]), .Y(T1));
+ else
+ assign T1 = A[A_WIDTH-1];
+ MUXF7 fpga_hard_mux (.I0(T0), .I1(T1), .S(B[2]), .O(Y));
+ end
+ else if (A_WIDTH <= 2 ** 4) begin
+ localparam a_width0 = 2 ** 2;
+ localparam num_mux8 = A_WIDTH / a_width0;
+ localparam a_widthN = A_WIDTH % a_width0;
+ wire [a_width0-1:0] T;
+ for (i = 0; i < a_width0; i++)
+ if (i < num_mux8)
+ \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_width0), .B_WIDTH(2), .Y_WIDTH(Y_WIDTH)) fpga_mux (.A(A[i*a_width0+:a_width0]), .B(B[2-1:0]), .Y(T[i]));
+ else if (i == num_mux8 && a_widthN > 1)
+ \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_widthN), .B_WIDTH($clog2(a_widthN)), .Y_WIDTH(Y_WIDTH)) fpga_mux_last (.A(A[A_WIDTH-1-:a_widthN]), .B(B[$clog2(a_widthN)-1:0]), .Y(T[i]));
+ else
+ assign T[i] = A[A_WIDTH-1];
+ \$__XILINX_MUXF78 fpga_hard_mux (.I0(T[0]), .I1(T[1]), .I2(T[2]), .I3(T[3]), .S0(B[2]), .S1(B[3]), .O(Y));
+ end
+ else begin
+ localparam a_width0 = 2 ** 4;
+ localparam num_mux16 = A_WIDTH / a_width0;
+ localparam a_widthN = A_WIDTH % a_width0;
+ wire [num_mux16 + (a_widthN > 0 ? 1 : 0) - 1:0] T;
+ for (i = 0; i < num_mux16; i++)
+ \$__XILINX_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_width0), .B_WIDTH(4), .Y_WIDTH(Y_WIDTH)) fpga_soft_mux (.A(A[i*a_width0+:a_width0]), .B(B[4-1:0]), .Y(T[i]));
+ if (a_widthN > 0) begin
+ if (a_widthN > 1)
+ \$__XILINX_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(a_widthN), .B_WIDTH($clog2(a_widthN)), .Y_WIDTH(Y_WIDTH)) fpga_soft_mux_last (.A(A[A_WIDTH-1-:a_widthN]), .B(B[$clog2(a_widthN)-1:0]), .Y(T[num_mux16]));
+ else
+ assign T[num_mux16] = A[A_WIDTH-1];
+ end
+ \$__XILINX_SHIFTX #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(num_mux16 + (a_widthN > 0 ? 1 : 0)), .B_WIDTH(B_WIDTH-4), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(T), .B(B[B_WIDTH-1:4]), .Y(Y));
+ end
+ endgenerate
+endmodule
+
+(* techmap_celltype = "$__XILINX_SHIFTX" *)
+module _90__XILINX_SHIFTX (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ \$shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(A_WIDTH), .B_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(A), .B(B), .Y(Y));
+endmodule
+
+module \$_MUX8_ (A, B, C, D, E, F, G, H, S, T, U, Y);
+input A, B, C, D, E, F, G, H, S, T, U;
+output Y;
+ \$__XILINX_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(8), .B_WIDTH(3), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({H,G,F,E,D,C,B,A}), .B({U,T,S}), .Y(Y));
+endmodule
+
+module \$_MUX16_ (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V, Y);
+input A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V;
+output Y;
+ \$__XILINX_SHIFTX #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(16), .B_WIDTH(4), .Y_WIDTH(1)) _TECHMAP_REPLACE_ (.A({P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A}), .B({V,U,T,S}), .Y(Y));
+endmodule
+`endif
+
+`ifndef _ABC
+module \$__XILINX_MUXF78 (O, I0, I1, I2, I3, S0, S1);
+ output O;
+ input I0, I1, I2, I3, S0, S1;
+ wire T0, T1;
+ parameter _TECHMAP_BITS_CONNMAP_ = 0;
+ parameter [_TECHMAP_BITS_CONNMAP_-1:0] _TECHMAP_CONNMAP_I0_ = 0;
+ parameter [_TECHMAP_BITS_CONNMAP_-1:0] _TECHMAP_CONNMAP_I1_ = 0;
+ parameter [_TECHMAP_BITS_CONNMAP_-1:0] _TECHMAP_CONNMAP_I2_ = 0;
+ parameter [_TECHMAP_BITS_CONNMAP_-1:0] _TECHMAP_CONNMAP_I3_ = 0;
+ parameter _TECHMAP_CONSTMSK_S0_ = 0;
+ parameter _TECHMAP_CONSTVAL_S0_ = 0;
+ parameter _TECHMAP_CONSTMSK_S1_ = 0;
+ parameter _TECHMAP_CONSTVAL_S1_ = 0;
+ if (_TECHMAP_CONSTMSK_S0_ && _TECHMAP_CONSTVAL_S0_ === 1'b1)
+ assign T0 = I1;
+ else if (_TECHMAP_CONSTMSK_S0_ || _TECHMAP_CONNMAP_I0_ === _TECHMAP_CONNMAP_I1_)
+ assign T0 = I0;
+ else
+ MUXF7 mux7a (.I0(I0), .I1(I1), .S(S0), .O(T0));
+ if (_TECHMAP_CONSTMSK_S0_ && _TECHMAP_CONSTVAL_S0_ === 1'b1)
+ assign T1 = I3;
+ else if (_TECHMAP_CONSTMSK_S0_ || _TECHMAP_CONNMAP_I2_ === _TECHMAP_CONNMAP_I3_)
+ assign T1 = I2;
+ else
+ MUXF7 mux7b (.I0(I2), .I1(I3), .S(S0), .O(T1));
+ if (_TECHMAP_CONSTMSK_S1_ && _TECHMAP_CONSTVAL_S1_ === 1'b1)
+ assign O = T1;
+ else if (_TECHMAP_CONSTMSK_S1_ || (_TECHMAP_CONNMAP_I0_ === _TECHMAP_CONNMAP_I1_ && _TECHMAP_CONNMAP_I1_ === _TECHMAP_CONNMAP_I2_ && _TECHMAP_CONNMAP_I2_ === _TECHMAP_CONNMAP_I3_))
+ assign O = T0;
+ else
+ MUXF8 mux8 (.I0(T0), .I1(T1), .S(S1), .O(O));
+endmodule
+`endif
diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v
index 8261286af..c6c49c3cd 100644
--- a/techlibs/xilinx/cells_sim.v
+++ b/techlibs/xilinx/cells_sim.v
@@ -169,6 +169,14 @@ module MUXF8(output O, input I0, I1, S);
assign O = S ? I1 : I0;
endmodule
+`ifdef _ABC
+(* abc_box_id = 10, lib_whitebox *)
+module \$__XILINX_MUXF78 (output O, input I0, I1, I2, I3, S0, S1);
+ assign O = S1 ? (S0 ? I3 : I2)
+ : (S0 ? I1 : I0);
+endmodule
+`endif
+
module XORCY(output O, input CI, LI);
assign O = CI ^ LI;
endmodule
@@ -281,6 +289,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/techlibs/xilinx/mux_map.v b/techlibs/xilinx/mux_map.v
new file mode 100644
index 000000000..91aaf2118
--- /dev/null
+++ b/techlibs/xilinx/mux_map.v
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ *
+ */
+
+// The purpose of these mapping rules is to allow preserve all (sufficiently
+// wide) $shiftx cells during 'techmap' so that they can be mapped to hard
+// resources, rather than being bit-blasted to gates during 'techmap'
+// execution
+
+module \$shiftx (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y;
+
+ parameter [B_WIDTH-1:0] _TECHMAP_CONSTMSK_B_ = 0;
+ parameter [B_WIDTH-1:0] _TECHMAP_CONSTVAL_B_ = 0;
+
+ generate
+ if (B_SIGNED) begin
+ if (_TECHMAP_CONSTMSK_B_[B_WIDTH-1] && (_TECHMAP_CONSTVAL_B_[B_WIDTH-1] == 1'b0 || _TECHMAP_CONSTVAL_B_[B_WIDTH-1] === 1'bx))
+ // Optimisation to remove B_SIGNED if sign bit of B is constant-0
+ \$shiftx #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(0),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH-1'd1),
+ .Y_WIDTH(Y_WIDTH)
+ ) _TECHMAP_REPLACE_ (
+ .A(A), .B(B[B_WIDTH-2:0]), .Y(Y)
+ );
+ else
+ wire _TECHMAP_FAIL_ = 1;
+ end
+ else begin
+ if (((A_WIDTH + Y_WIDTH - 1) / Y_WIDTH) < `MIN_MUX_INPUTS)
+ wire _TECHMAP_FAIL_ = 1;
+ else
+ \$__XILINX_SHIFTX #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+ ) _TECHMAP_REPLACE_ (
+ .A(A), .B(B), .Y(Y)
+ );
+ end
+ endgenerate
+endmodule
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc
index 86b49b13c..69b1580b8 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -73,6 +73,11 @@ struct SynthXilinxPass : public ScriptPass
log(" -nosrl\n");
log(" disable inference of shift registers\n");
log("\n");
+ log(" -minmuxf <int>\n");
+ log(" enable inference of hard multiplexer resources (MuxFx) for muxes at or\n");
+ log(" above this number of inputs (minimum value 5).\n");
+ log(" default: 0 (no inference)\n");
+ log("\n");
log(" -run <from_label>:<to_label>\n");
log(" only run the commands between the labels (see below). an empty\n");
log(" from label is synonymous to 'begin', and empty to label is\n");
@@ -95,6 +100,7 @@ struct SynthXilinxPass : public ScriptPass
std::string top_opt, edif_file, blif_file, abc, arch;
bool flatten, retime, vpr, nocarry, nobram, nodram, nosrl;
+ int minmuxf;
void clear_flags() YS_OVERRIDE
{
@@ -110,6 +116,7 @@ struct SynthXilinxPass : public ScriptPass
nodram = false;
nosrl = false;
arch = "xc7";
+ minmuxf = 0;
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -172,6 +179,10 @@ struct SynthXilinxPass : public ScriptPass
nosrl = true;
continue;
}
+ if (args[argidx] == "-minmuxf" && argidx+1 < args.size()) {
+ minmuxf = atoi(args[++argidx].c_str());
+ continue;
+ }
if (args[argidx] == "-abc9") {
abc = "abc9";
continue;
@@ -183,6 +194,9 @@ struct SynthXilinxPass : public ScriptPass
if (arch != "xcup" && arch != "xcu" && arch != "xc7" && arch != "xc6s")
log_cmd_error("Invalid Xilinx -arch setting: %s\n", arch.c_str());
+ if (minmuxf != 0 && minmuxf < 5)
+ log_cmd_error("-minmuxf value must be 0 or >= 5.\n");
+
if (!design->full_selection())
log_cmd_error("This command only operates on fully selected designs!\n");
@@ -198,9 +212,9 @@ struct SynthXilinxPass : public ScriptPass
{
if (check_label("begin")) {
if (vpr)
- run("read_verilog -lib -D _ABC -D_EXPLICIT_CARRY +/xilinx/cells_sim.v");
+ run("read_verilog -lib -icells -D _ABC -D_EXPLICIT_CARRY +/xilinx/cells_sim.v");
else
- run("read_verilog -lib -D _ABC +/xilinx/cells_sim.v");
+ run("read_verilog -lib -icells -D _ABC +/xilinx/cells_sim.v");
run("read_verilog -lib +/xilinx/cells_xtra.v");
@@ -210,26 +224,37 @@ struct SynthXilinxPass : public ScriptPass
run(stringf("hierarchy -check %s", top_opt.c_str()));
}
- if (check_label("flatten", "(with '-flatten' only)")) {
- if (flatten || help_mode) {
- run("proc");
- run("flatten");
- }
- }
-
if (check_label("coarse")) {
- run("synth -run coarse");
+ run("proc");
+ if (flatten || help_mode)
+ run("flatten", "(with -flatten only)");
+ run("opt_expr");
+ run("opt_clean");
+ run("check");
+ run("opt");
+ if (help_mode)
+ run("wreduce [c:* t:$mux %d]", "(selection for '-minmuxf' only)");
+ else
+ run("wreduce" + std::string(minmuxf > 0 ? " c:* t:$mux %d" : ""));
+ run("peepopt");
+ run("opt_clean");
+ run("alumacc");
+ run("share");
+ run("opt");
+ run("fsm");
+ run("opt -fast");
+ run("memory -nomap");
+ run("opt_clean");
+
+ if (minmuxf > 0 || help_mode)
+ run("muxpack", " ('-minmuxf' only)");
// shregmap -tech xilinx can cope with $shiftx and $mux
// cells for identifying variable-length shift registers,
// so attempt to convert $pmux-es to the former
- if (!nosrl || help_mode)
- run("pmux2shiftx", "(skip if '-nosrl')");
-
- // Run a number of peephole optimisations, including one
- // that optimises $mul cells driving $shiftx's B input
- // and that aids wide mux analysis
- run("peepopt");
+ // Also: wide multiplexer inference benefits from this too
+ if (!(nosrl && minmuxf == 0) || help_mode)
+ run("pmux2shiftx", "(skip if '-nosrl' and '-minmuxf' < 5)");
}
if (check_label("bram", "(skip if '-nobram')")) {
@@ -247,36 +272,67 @@ struct SynthXilinxPass : public ScriptPass
}
if (check_label("fine")) {
- run("opt -fast -full");
+ run("opt -fast");
run("memory_map");
run("dffsr2dff");
run("dff2dffe");
+ if (minmuxf > 0 || help_mode) {
+ run("simplemap t:$mux", " ('-minmuxf' only)");
+ if (minmuxf > 0 || help_mode) {
+ std::string muxcover_args = " -dmux=0";
+ switch (minmuxf) {
+ // NB: Cost of mux2 is 100; mux8 should cost between 3 and 4
+ // of those so that 4:1 muxes and below are implemented
+ // out of mux2s
+ case 5: muxcover_args += " -mux8=350 -mux16=400"; break;
+ case 6: muxcover_args += " -mux8=450 -mux16=500"; break;
+ case 7: muxcover_args += " -mux8=550 -mux16=600"; break;
+ case 8: muxcover_args += " -mux8=650 -mux16=700"; break;
+ case 9: muxcover_args += " -mux16=750"; break;
+ case 10: muxcover_args += " -mux16=850"; break;
+ case 11: muxcover_args += " -mux16=950"; break;
+ case 12: muxcover_args += " -mux16=1050"; break;
+ case 13: muxcover_args += " -mux16=1150"; break;
+ case 14: muxcover_args += " -mux16=1250"; break;
+ case 15: muxcover_args += " -mux16=1350"; break;
+ default: muxcover_args += " -mux16=1450"; break;
+ }
+ run("muxcover " + muxcover_args, "('-minmuxf' only)");
+ }
+ }
run("opt -full");
if (!nosrl || help_mode) {
// shregmap operates on bit-level flops, not word-level,
// so break those down here
- run("simplemap t:$dff t:$dffe", "(skip if '-nosrl')");
+ run("simplemap t:$dff t:$dffe", " (skip if '-nosrl')");
// shregmap with '-tech xilinx' infers variable length shift regs
run("shregmap -tech xilinx -minlen 3", "(skip if '-nosrl')");
}
- std::string techmap_files = " -map +/techmap.v";
+ std::string techmap_args = " -map +/techmap.v";
+ if (help_mode)
+ techmap_args += " [-map +/xilinx/mux_map.v]";
+ else if (minmuxf > 0)
+ techmap_args += stringf(" -D MIN_MUX_INPUTS=%d -map +/xilinx/mux_map.v", minmuxf);
if (help_mode)
- techmap_files += " [-map +/xilinx/arith_map.v]";
+ techmap_args += " [-map +/xilinx/arith_map.v]";
else if (!nocarry) {
- techmap_files += " -map +/xilinx/arith_map.v";
+ techmap_args += " -map +/xilinx/arith_map.v";
if (vpr)
- techmap_files += " -D _EXPLICIT_CARRY";
+ techmap_args += " -D _EXPLICIT_CARRY";
else if (abc == "abc9")
- techmap_files += " -D _CLB_CARRY";
+ techmap_args += " -D _CLB_CARRY";
}
- run("techmap " + techmap_files);
+ run("techmap " + techmap_args);
run("opt -fast");
}
if (check_label("map_cells")) {
- run("techmap -map +/techmap.v -map +/xilinx/cells_map.v");
+ std::string techmap_args = "-map +/techmap.v -D _ABC -map +/xilinx/cells_map.v";
+ if (minmuxf > 0)
+ techmap_args += stringf(" -D MIN_MUX_INPUTS=%d", minmuxf);
+ run("techmap " + techmap_args);
run("clean");
}
diff --git a/tests/various/muxpack.v b/tests/various/muxpack.v
new file mode 100644
index 000000000..3a1086dbf
--- /dev/null
+++ b/tests/various/muxpack.v
@@ -0,0 +1,199 @@
+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, 2: o = b;
+ 1: o = c;
+ default: begin
+ o = a;
+ if (y == 0) o = d;
+ if (y == 1) o = e;
+ end
+ endcase
+ end
+endmodule
diff --git a/tests/various/muxpack.ys b/tests/various/muxpack.ys
new file mode 100644
index 000000000..579dad8d3
--- /dev/null
+++ b/tests/various/muxpack.ys
@@ -0,0 +1,214 @@
+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