From 93508d58dafbbffcedffa70b21a197b6fca8bb30 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= <mwk@0x04.net>
Date: Mon, 24 Jan 2022 16:02:29 +0100
Subject: Add $bmux and $demux cells.

---
 backends/btor/btor.cc               |   5 ++
 backends/cxxrtl/cxxrtl.h            |  36 +++++++++++++
 backends/cxxrtl/cxxrtl_backend.cc   |  18 ++++++-
 backends/firrtl/firrtl.cc           |   6 ++-
 backends/smt2/smt2.cc               |   5 ++
 backends/smv/smv.cc                 |   5 ++
 backends/verilog/verilog_backend.cc |   4 ++
 kernel/calc.cc                      |  51 ++++++++++++++++++
 kernel/celledges.cc                 |  40 +++++++++++++++
 kernel/celltypes.h                  |  29 +++++------
 kernel/consteval.h                  |  35 ++++++++++++-
 kernel/qcsat.cc                     |   2 +-
 kernel/rtlil.cc                     |  47 ++++++++++++++++-
 kernel/rtlil.h                      |   7 +++
 kernel/satgen.cc                    | 100 ++++++++++++++++++++++++++++++++++++
 passes/cmds/clean_zerowidth.cc      |   2 +-
 passes/cmds/stat.cc                 |   4 ++
 passes/sat/sim.cc                   |   6 +++
 passes/techmap/Makefile.inc         |   2 +
 passes/techmap/bmuxmap.cc           |  76 +++++++++++++++++++++++++++
 passes/techmap/demuxmap.cc          |  80 +++++++++++++++++++++++++++++
 passes/techmap/simplemap.cc         |  26 +++++++++-
 passes/tests/test_cell.cc           |  46 ++++++++++++++++-
 techlibs/common/simlib.v            |  72 +++++++++++++++++---------
 techlibs/common/techmap.v           |  39 +++++++++++++-
 25 files changed, 694 insertions(+), 49 deletions(-)
 create mode 100644 passes/techmap/bmuxmap.cc
 create mode 100644 passes/techmap/demuxmap.cc

diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc
index 96df54a2c..d62cc4c3d 100644
--- a/backends/btor/btor.cc
+++ b/backends/btor/btor.cc
@@ -1399,6 +1399,11 @@ struct BtorBackend : public Backend {
 
 		log_header(design, "Executing BTOR backend.\n");
 
+		log_push();
+		Pass::call(design, "bmuxmap");
+		Pass::call(design, "demuxmap");
+		log_pop();
+
 		size_t argidx;
 		for (argidx = 1; argidx < args.size(); argidx++)
 		{
diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h
index 3e1357498..b4ffa87cd 100644
--- a/backends/cxxrtl/cxxrtl.h
+++ b/backends/cxxrtl/cxxrtl.h
@@ -457,6 +457,42 @@ struct value : public expr_base<value<Bits>> {
 		return shr<AmountBits, /*Signed=*/true>(amount);
 	}
 
+	template<size_t ResultBits, size_t SelBits>
+	value<ResultBits> bmux(const value<SelBits> &sel) const {
+		static_assert(ResultBits << SelBits == Bits, "invalid sizes used in bmux()");
+		size_t amount = sel.data[0] * ResultBits;
+		size_t shift_chunks = amount / chunk::bits;
+		size_t shift_bits   = amount % chunk::bits;
+		value<ResultBits> result;
+		chunk::type carry = 0;
+		if (ResultBits % chunk::bits + shift_bits > chunk::bits)
+			carry = data[result.chunks + shift_chunks] << (chunk::bits - shift_bits);
+		for (size_t n = 0; n < result.chunks; n++) {
+			result.data[result.chunks - 1 - n] = carry | (data[result.chunks + shift_chunks - 1 - n] >> shift_bits);
+			carry = (shift_bits == 0) ? 0
+				: data[result.chunks + shift_chunks - 1 - n] << (chunk::bits - shift_bits);
+		}
+		return result;
+	}
+
+	template<size_t ResultBits, size_t SelBits>
+	value<ResultBits> demux(const value<SelBits> &sel) const {
+		static_assert(Bits << SelBits == ResultBits, "invalid sizes used in demux()");
+		size_t amount = sel.data[0] * Bits;
+		size_t shift_chunks = amount / chunk::bits;
+		size_t shift_bits   = amount % chunk::bits;
+		value<ResultBits> result;
+		chunk::type carry = 0;
+		for (size_t n = 0; n < chunks; n++) {
+			result.data[shift_chunks + n] = (data[n] << shift_bits) | carry;
+			carry = (shift_bits == 0) ? 0
+				: data[n] >> (chunk::bits - shift_bits);
+		}
+		if (Bits % chunk::bits + shift_bits > chunk::bits)
+			result.data[shift_chunks + chunks] = carry;
+		return result;
+	}
+
 	size_t ctpop() const {
 		size_t count = 0;
 		for (size_t n = 0; n < chunks; n++) {
diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc
index 7a336f8c1..404755b1e 100644
--- a/backends/cxxrtl/cxxrtl_backend.cc
+++ b/backends/cxxrtl/cxxrtl_backend.cc
@@ -198,7 +198,7 @@ bool is_extending_cell(RTLIL::IdString type)
 bool is_inlinable_cell(RTLIL::IdString type)
 {
 	return is_unary_cell(type) || is_binary_cell(type) || type.in(
-		ID($mux), ID($concat), ID($slice), ID($pmux));
+		ID($mux), ID($concat), ID($slice), ID($pmux), ID($bmux), ID($demux));
 }
 
 bool is_ff_cell(RTLIL::IdString type)
@@ -1154,6 +1154,22 @@ struct CxxrtlWorker {
 			for (int part = 0; part < s_width; part++) {
 				f << ")";
 			}
+		// Big muxes
+		} else if (cell->type == ID($bmux)) {
+			dump_sigspec_rhs(cell->getPort(ID::A), for_debug);
+			f << ".bmux<";
+			f << cell->getParam(ID::WIDTH).as_int();
+			f << ">(";
+			dump_sigspec_rhs(cell->getPort(ID::S), for_debug);
+			f << ").val()";
+		// Demuxes
+		} else if (cell->type == ID($demux)) {
+			dump_sigspec_rhs(cell->getPort(ID::A), for_debug);
+			f << ".demux<";
+			f << GetSize(cell->getPort(ID::Y));
+			f << ">(";
+			dump_sigspec_rhs(cell->getPort(ID::S), for_debug);
+			f << ").val()";
 		// Concats
 		} else if (cell->type == ID($concat)) {
 			dump_sigspec_rhs(cell->getPort(ID::B), for_debug);
diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc
index 7abe584c9..85c44824f 100644
--- a/backends/firrtl/firrtl.cc
+++ b/backends/firrtl/firrtl.cc
@@ -1188,6 +1188,8 @@ struct FirrtlBackend : public Backend {
 		log("Write a FIRRTL netlist of the current design.\n");
 		log("The following commands are executed by this command:\n");
 		log("        pmuxtree\n");
+		log("        bmuxmap\n");
+		log("        demuxmap\n");
 		log("\n");
 	}
 	void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
@@ -1210,7 +1212,9 @@ struct FirrtlBackend : public Backend {
 		log_header(design, "Executing FIRRTL backend.\n");
 		log_push();
 
-		Pass::call(design, stringf("pmuxtree"));
+		Pass::call(design, "pmuxtree");
+		Pass::call(design, "bmuxmap");
+		Pass::call(design, "demuxmap");
 
 		namecache.clear();
 		autoid_counter = 0;
diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc
index f2fa003bc..a928419a1 100644
--- a/backends/smt2/smt2.cc
+++ b/backends/smt2/smt2.cc
@@ -1531,6 +1531,11 @@ struct Smt2Backend : public Backend {
 
 		log_header(design, "Executing SMT2 backend.\n");
 
+		log_push();
+		Pass::call(design, "bmuxmap");
+		Pass::call(design, "demuxmap");
+		log_pop();
+
 		size_t argidx;
 		for (argidx = 1; argidx < args.size(); argidx++)
 		{
diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc
index 7bace6912..7d4f94adc 100644
--- a/backends/smv/smv.cc
+++ b/backends/smv/smv.cc
@@ -741,6 +741,11 @@ struct SmvBackend : public Backend {
 
 		log_header(design, "Executing SMV backend.\n");
 
+		log_push();
+		Pass::call(design, "bmuxmap");
+		Pass::call(design, "demuxmap");
+		log_pop();
+
 		size_t argidx;
 		for (argidx = 1; argidx < args.size(); argidx++)
 		{
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
index e4781ef3e..32003cf54 100644
--- a/backends/verilog/verilog_backend.cc
+++ b/backends/verilog/verilog_backend.cc
@@ -2300,7 +2300,11 @@ struct VerilogBackend : public Backend {
 			extmem_prefix = filename.substr(0, filename.rfind('.'));
 		}
 
+		log_push();
+		Pass::call(design, "bmuxmap");
+		Pass::call(design, "demuxmap");
 		Pass::call(design, "clean_zerowidth");
+		log_pop();
 
 		design->sort();
 
diff --git a/kernel/calc.cc b/kernel/calc.cc
index 1e6410f7d..0865db526 100644
--- a/kernel/calc.cc
+++ b/kernel/calc.cc
@@ -609,5 +609,56 @@ RTLIL::Const RTLIL::const_neg(const RTLIL::Const &arg1, const RTLIL::Const&, boo
 	return RTLIL::const_sub(zero, arg1_ext, true, signed1, result_len);
 }
 
+RTLIL::Const RTLIL::const_bmux(const RTLIL::Const &arg1, const RTLIL::Const &arg2)
+{
+	std::vector<RTLIL::State> t = arg1.bits;
+
+	for (int i = GetSize(arg2)-1; i >= 0; i--)
+	{
+		RTLIL::State sel = arg2.bits.at(i);
+		std::vector<RTLIL::State> new_t;
+		if (sel == State::S0)
+			new_t = std::vector<RTLIL::State>(t.begin(), t.begin() + GetSize(t)/2);
+		else if (sel == State::S1)
+			new_t = std::vector<RTLIL::State>(t.begin() + GetSize(t)/2, t.end());
+		else
+			for (int j = 0; j < GetSize(t)/2; j++)
+				new_t.push_back(t[j] == t[j + GetSize(t)/2] ? t[j] : RTLIL::Sx);
+		t.swap(new_t);
+	}
+
+	return t;
+}
+
+RTLIL::Const RTLIL::const_demux(const RTLIL::Const &arg1, const RTLIL::Const &arg2)
+{
+	int width = GetSize(arg1);
+	int s_width = GetSize(arg2);
+	std::vector<RTLIL::State> res;
+	for (int i = 0; i < (1 << s_width); i++)
+	{
+		bool ne = false;
+		bool x = false;
+		for (int j = 0; j < s_width; j++) {
+			bool bit = i & 1 << j;
+			if (arg2[j] == (bit ? RTLIL::S0 : RTLIL::S1))
+				ne = true;
+			else if (arg2[j] != RTLIL::S0 && arg2[j] != RTLIL::S1)
+				x = true;
+		}
+		if (ne) {
+			for (int j = 0; j < width; j++)
+				res.push_back(State::S0);
+		} else if (x) {
+			for (int j = 0; j < width; j++)
+				res.push_back(arg1.bits[j] == State::S0 ? State::S0 : State::Sx);
+		} else {
+			for (int j = 0; j < width; j++)
+				res.push_back(arg1.bits[j]);
+		}
+	}
+	return res;
+}
+
 YOSYS_NAMESPACE_END
 
diff --git a/kernel/celledges.cc b/kernel/celledges.cc
index af07d26b3..c43ba8db3 100644
--- a/kernel/celledges.cc
+++ b/kernel/celledges.cc
@@ -142,6 +142,36 @@ void mux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
 	}
 }
 
+void bmux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
+{
+	int width = GetSize(cell->getPort(ID::Y));
+	int a_width = GetSize(cell->getPort(ID::A));
+	int s_width = GetSize(cell->getPort(ID::S));
+
+	for (int i = 0; i < width; i++)
+	{
+		for (int k = i; k < a_width; k += width)
+			db->add_edge(cell, ID::A, k, ID::Y, i, -1);
+
+		for (int k = 0; k < s_width; k++)
+			db->add_edge(cell, ID::S, k, ID::Y, i, -1);
+	}
+}
+
+void demux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
+{
+	int width = GetSize(cell->getPort(ID::Y));
+	int a_width = GetSize(cell->getPort(ID::A));
+	int s_width = GetSize(cell->getPort(ID::S));
+
+	for (int i = 0; i < width; i++)
+	{
+		db->add_edge(cell, ID::A, i % a_width, ID::Y, i, -1);
+		for (int k = 0; k < s_width; k++)
+			db->add_edge(cell, ID::S, k, ID::Y, i, -1);
+	}
+}
+
 PRIVATE_NAMESPACE_END
 
 bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell)
@@ -187,6 +217,16 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL
 		return true;
 	}
 
+	if (cell->type == ID($bmux)) {
+		bmux_op(this, cell);
+		return true;
+	}
+
+	if (cell->type == ID($demux)) {
+		demux_op(this, cell);
+		return true;
+	}
+
 	// FIXME: $mul $div $mod $divfloor $modfloor $slice $concat
 	// FIXME: $lut $sop $alu $lcu $macc $fa
 
diff --git a/kernel/celltypes.h b/kernel/celltypes.h
index 879ac0edc..7e9cfb38d 100644
--- a/kernel/celltypes.h
+++ b/kernel/celltypes.h
@@ -127,6 +127,9 @@ struct CellTypes
 		for (auto type : std::vector<RTLIL::IdString>({ID($mux), ID($pmux)}))
 			setup_type(type, {ID::A, ID::B, ID::S}, {ID::Y}, true);
 
+		for (auto type : std::vector<RTLIL::IdString>({ID($bmux), ID($demux)}))
+			setup_type(type, {ID::A, ID::S}, {ID::Y}, true);
+
 		setup_type(ID($lcu), {ID::P, ID::G, ID::CI}, {ID::CO}, true);
 		setup_type(ID($alu), {ID::A, ID::B, ID::CI, ID::BI}, {ID::X, ID::Y, ID::CO}, true);
 		setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, true);
@@ -411,6 +414,16 @@ struct CellTypes
 			return ret;
 		}
 
+		if (cell->type == ID($bmux))
+		{
+			return const_bmux(arg1, arg2);
+		}
+
+		if (cell->type == ID($demux))
+		{
+			return const_demux(arg1, arg2);
+		}
+
 		if (cell->type == ID($lut))
 		{
 			int width = cell->parameters.at(ID::WIDTH).as_int();
@@ -420,21 +433,7 @@ struct CellTypes
 				t.push_back(State::S0);
 			t.resize(1 << width);
 
-			for (int i = width-1; i >= 0; i--) {
-				RTLIL::State sel = arg1.bits.at(i);
-				std::vector<RTLIL::State> new_t;
-				if (sel == State::S0)
-					new_t = std::vector<RTLIL::State>(t.begin(), t.begin() + GetSize(t)/2);
-				else if (sel == State::S1)
-					new_t = std::vector<RTLIL::State>(t.begin() + GetSize(t)/2, t.end());
-				else
-					for (int j = 0; j < GetSize(t)/2; j++)
-						new_t.push_back(t[j] == t[j + GetSize(t)/2] ? t[j] : RTLIL::Sx);
-				t.swap(new_t);
-			}
-
-			log_assert(GetSize(t) == 1);
-			return t;
+			return const_bmux(t, arg1);
 		}
 
 		if (cell->type == ID($sop))
diff --git a/kernel/consteval.h b/kernel/consteval.h
index 3edfc490c..642eb42b2 100644
--- a/kernel/consteval.h
+++ b/kernel/consteval.h
@@ -135,8 +135,6 @@ struct ConstEval
 
 		if (cell->hasPort(ID::S)) {
 			sig_s = cell->getPort(ID::S);
-			if (!eval(sig_s, undef, cell))
-				return false;
 		}
 
 		if (cell->hasPort(ID::A))
@@ -151,6 +149,9 @@ struct ConstEval
 			int count_maybe_set_s_bits = 0;
 			int count_set_s_bits = 0;
 
+			if (!eval(sig_s, undef, cell))
+				return false;
+
 			for (int i = 0; i < sig_s.size(); i++)
 			{
 				RTLIL::State s_bit = sig_s.extract(i, 1).as_const().bits.at(0);
@@ -198,6 +199,36 @@ struct ConstEval
 			else
 				set(sig_y, y_values.front());
 		}
+		else if (cell->type == ID($bmux))
+		{
+			if (!eval(sig_s, undef, cell))
+				return false;
+
+			if (sig_s.is_fully_def()) {
+				int sel = sig_s.as_int();
+				int width = GetSize(sig_y);
+				SigSpec res = sig_a.extract(sel * width, width);
+				if (!eval(res, undef, cell))
+					return false;
+				set(sig_y, res.as_const());
+			} else {
+				if (!eval(sig_a, undef, cell))
+					return false;
+				set(sig_y, const_bmux(sig_a.as_const(), sig_s.as_const()));
+			}
+		}
+		else if (cell->type == ID($demux))
+		{
+			if (!eval(sig_a, undef, cell))
+				return false;
+			if (sig_a.is_fully_zero()) {
+				set(sig_y, Const(0, GetSize(sig_y)));
+			} else {
+				if (!eval(sig_s, undef, cell))
+					return false;
+				set(sig_y, const_demux(sig_a.as_const(), sig_s.as_const()));
+			}
+		}
 		else if (cell->type == ID($fa))
 		{
 			RTLIL::SigSpec sig_c = cell->getPort(ID::C);
diff --git a/kernel/qcsat.cc b/kernel/qcsat.cc
index b7da958db..aaee984fb 100644
--- a/kernel/qcsat.cc
+++ b/kernel/qcsat.cc
@@ -84,7 +84,7 @@ int QuickConeSat::cell_complexity(RTLIL::Cell *cell)
 			ID($reduce_xnor), ID($reduce_bool),
 			ID($logic_not), ID($logic_and), ID($logic_or),
 			ID($eq), ID($ne), ID($eqx), ID($nex), ID($fa),
-			ID($mux), ID($pmux), ID($lut), ID($sop),
+			ID($mux), ID($pmux), ID($bmux), ID($demux), ID($lut), ID($sop),
 			ID($_NOT_), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_),
 			ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_),
 			ID($_MUX_), ID($_NMUX_), ID($_MUX4_), ID($_MUX8_), ID($_MUX16_),
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index cd0f5ab12..a89edd992 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -1251,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));
@@ -2444,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);         \
@@ -3358,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;
diff --git a/kernel/rtlil.h b/kernel/rtlil.h
index a562d253c..d8300f159 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -485,6 +485,9 @@ namespace RTLIL
 	RTLIL::Const const_pos         (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
 	RTLIL::Const const_neg         (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
 
+	RTLIL::Const const_bmux        (const RTLIL::Const &arg1, const RTLIL::Const &arg2);
+	RTLIL::Const const_demux       (const RTLIL::Const &arg1, const RTLIL::Const &arg2);
+
 
 	// This iterator-range-pair is used for Design::modules(), Module::wires() and Module::cells().
 	// It maintains a reference counter that is used to make sure that the container is not modified while being iterated over.
@@ -1296,6 +1299,8 @@ public:
 
 	RTLIL::Cell* addMux  (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = "");
 	RTLIL::Cell* addPmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = "");
+	RTLIL::Cell* addBmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = "");
+	RTLIL::Cell* addDemux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = "");
 
 	RTLIL::Cell* addSlice  (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, RTLIL::Const offset, const std::string &src = "");
 	RTLIL::Cell* addConcat (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, const std::string &src = "");
@@ -1421,6 +1426,8 @@ public:
 
 	RTLIL::SigSpec Mux      (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const std::string &src = "");
 	RTLIL::SigSpec Pmux     (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const std::string &src = "");
+	RTLIL::SigSpec Bmux     (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const std::string &src = "");
+	RTLIL::SigSpec Demux     (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const std::string &src = "");
 
 	RTLIL::SigBit BufGate    (RTLIL::IdString name, const RTLIL::SigBit &sig_a, const std::string &src = "");
 	RTLIL::SigBit NotGate    (RTLIL::IdString name, const RTLIL::SigBit &sig_a, const std::string &src = "");
diff --git a/kernel/satgen.cc b/kernel/satgen.cc
index 214826f5a..9c40ec66d 100644
--- a/kernel/satgen.cc
+++ b/kernel/satgen.cc
@@ -252,6 +252,106 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
 		return true;
 	}
 
+	if (cell->type == ID($bmux))
+	{
+		std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+		std::vector<int> s = importDefSigSpec(cell->getPort(ID::S), timestep);
+		std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+		std::vector<int> undef_a, undef_s, undef_y;
+
+		if (model_undef)
+		{
+			undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+			undef_s = importUndefSigSpec(cell->getPort(ID::S), timestep);
+			undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+		}
+
+		if (GetSize(s) == 0) {
+			ez->vec_set(a, y);
+			if (model_undef)
+				ez->vec_set(undef_a, undef_y);
+		} else {
+			for (int i = GetSize(s)-1; i >= 0; i--)
+			{
+				std::vector<int> out = (i == 0) ? y : ez->vec_var(a.size() / 2);
+				std::vector<int> yy = model_undef ? ez->vec_var(out.size()) : out;
+
+				std::vector<int> a0(a.begin(), a.begin() + a.size() / 2);
+				std::vector<int> a1(a.begin() + a.size() / 2, a.end());
+				ez->assume(ez->vec_eq(ez->vec_ite(s.at(i), a1, a0), yy));
+
+				if (model_undef)
+				{
+					std::vector<int> undef_out = (i == 0) ? undef_y : ez->vec_var(a.size() / 2);
+					std::vector<int> undef_a0(undef_a.begin(), undef_a.begin() + a.size() / 2);
+					std::vector<int> undef_a1(undef_a.begin() + a.size() / 2, undef_a.end());
+					std::vector<int> unequal_ab = ez->vec_not(ez->vec_iff(a0, a1));
+					std::vector<int> undef_ab = ez->vec_or(unequal_ab, ez->vec_or(undef_a0, undef_a1));
+					std::vector<int> yX = ez->vec_ite(undef_s.at(i), undef_ab, ez->vec_ite(s.at(i), undef_a1, undef_a0));
+					ez->assume(ez->vec_eq(yX, undef_out));
+					undefGating(out, yy, undef_out);
+
+					undef_a = undef_out;
+				}
+
+				a = out;
+			}
+		}
+		return true;
+	}
+
+	if (cell->type == ID($demux))
+	{
+		std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+		std::vector<int> s = importDefSigSpec(cell->getPort(ID::S), timestep);
+		std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+		std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+		std::vector<int> undef_a, undef_s, undef_y;
+
+		if (model_undef)
+		{
+			undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+			undef_s = importUndefSigSpec(cell->getPort(ID::S), timestep);
+			undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+		}
+
+		if (GetSize(s) == 0) {
+			ez->vec_set(a, y);
+			if (model_undef)
+				ez->vec_set(undef_a, undef_y);
+		} else {
+			for (int i = 0; i < (1 << GetSize(s)); i++)
+			{
+				std::vector<int> ss;
+				for (int j = 0; j < GetSize(s); j++) {
+					if (i & 1 << j)
+						ss.push_back(s[j]);
+					else
+						ss.push_back(ez->NOT(s[j]));
+				}
+				int sss = ez->expression(ezSAT::OpAnd, ss);
+
+				for (int j = 0; j < GetSize(a); j++) {
+					ez->SET(ez->AND(sss, a[j]), yy.at(i * GetSize(a) + j));
+				}
+
+				if (model_undef)
+				{
+					int s0 = ez->expression(ezSAT::OpOr, ez->vec_and(ez->vec_not(ss), ez->vec_not(undef_s)));
+					int us = ez->AND(ez->NOT(s0), ez->expression(ezSAT::OpOr, undef_s));
+					for (int j = 0; j < GetSize(a); j++) {
+						int a0 = ez->AND(ez->NOT(a[j]), ez->NOT(undef_a[j]));
+						int yX = ez->AND(ez->OR(us, undef_a[j]), ez->NOT(ez->OR(s0, a0)));
+						ez->SET(yX, undef_y.at(i * GetSize(a) + j));
+					}
+				}
+			}
+			if (model_undef)
+				undefGating(y, yy, undef_y);
+		}
+		return true;
+	}
+
 	if (cell->type == ID($pmux))
 	{
 		std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
diff --git a/passes/cmds/clean_zerowidth.cc b/passes/cmds/clean_zerowidth.cc
index 4e7c68093..bac6b1521 100644
--- a/passes/cmds/clean_zerowidth.cc
+++ b/passes/cmds/clean_zerowidth.cc
@@ -80,7 +80,7 @@ struct CleanZeroWidthPass : public Pass {
 					if (GetSize(cell->getPort(ID::Q)) == 0) {
 						module->remove(cell);
 					}
-				} else if (cell->type == ID($pmux)) {
+				} else if (cell->type.in(ID($pmux), ID($bmux), ID($demux))) {
 					// Remove altogether if WIDTH is 0, replace with
 					// a connection if S_WIDTH is 0.
 					if (cell->getParam(ID::WIDTH).as_int() == 0) {
diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc
index 14a27ed99..fffdda48e 100644
--- a/passes/cmds/stat.cc
+++ b/passes/cmds/stat.cc
@@ -117,6 +117,10 @@ struct statdata_t
 				}
 				else if (cell_type.in(ID($mux), ID($pmux)))
 					cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Y)));
+				else if (cell_type == ID($bmux))
+					cell_type = stringf("%s_%d_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Y)), GetSize(cell->getPort(ID::S)));
+				else if (cell_type == ID($demux))
+					cell_type = stringf("%s_%d_%d", cell_type.c_str(), GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::S)));
 				else if (cell_type.in(
 						ID($sr), ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre),
 						ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce),
diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc
index 4e158da62..8a44830d2 100644
--- a/passes/sat/sim.cc
+++ b/passes/sat/sim.cc
@@ -313,6 +313,12 @@ struct SimInstance
 				return;
 			}
 
+			// (A,S -> Y) cells
+			if (has_a && !has_b && !has_c && !has_d && has_s && has_y) {
+				set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_s)));
+				return;
+			}
+
 			// (A,B,S -> Y) cells
 			if (has_a && has_b && !has_c && !has_d && has_s && has_y) {
 				set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_s)));
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index 035699603..98ccfc303 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -29,6 +29,8 @@ OBJS += passes/techmap/extract_reduce.o
 OBJS += passes/techmap/alumacc.o
 OBJS += passes/techmap/dffinit.o
 OBJS += passes/techmap/pmuxtree.o
+OBJS += passes/techmap/bmuxmap.o
+OBJS += passes/techmap/demuxmap.o
 OBJS += passes/techmap/muxcover.o
 OBJS += passes/techmap/aigmap.o
 OBJS += passes/techmap/tribuf.o
diff --git a/passes/techmap/bmuxmap.cc b/passes/techmap/bmuxmap.cc
new file mode 100644
index 000000000..03673c278
--- /dev/null
+++ b/passes/techmap/bmuxmap.cc
@@ -0,0 +1,76 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2022  Marcelina Koƛcielnicka <mwk@0x04.net>
+ *
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct BmuxmapPass : public Pass {
+	BmuxmapPass() : Pass("bmuxmap", "transform $bmux cells to trees of $mux cells") { }
+	void help() override
+	{
+		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+		log("\n");
+		log("    bmuxmap [selection]\n");
+		log("\n");
+		log("This pass transforms $bmux cells to trees of $mux cells.\n");
+		log("\n");
+	}
+	void execute(std::vector<std::string> args, RTLIL::Design *design) override
+	{
+		log_header(design, "Executing BMUXMAP pass.\n");
+
+		size_t argidx;
+		for (argidx = 1; argidx < args.size(); argidx++) {
+			break;
+		}
+		extra_args(args, argidx, design);
+
+		for (auto module : design->selected_modules())
+		for (auto cell : module->selected_cells())
+		{
+			if (cell->type != ID($bmux))
+				continue;
+
+			SigSpec sel = cell->getPort(ID::S);
+			SigSpec data = cell->getPort(ID::A);
+			int width = GetSize(cell->getPort(ID::Y));
+
+			for (int idx = 0; idx < GetSize(sel); idx++) {
+				SigSpec new_data = module->addWire(NEW_ID, GetSize(data)/2);
+				for (int i = 0; i < GetSize(new_data); i += width) {
+					RTLIL::Cell *mux = module->addMux(NEW_ID,
+							data.extract(i*2, width),
+							data.extract(i*2+width, width),
+							sel[idx],
+							new_data.extract(i, width));
+					mux->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
+				}
+				data = new_data;
+			}
+
+			module->connect(cell->getPort(ID::Y), data);
+			module->remove(cell);
+		}
+	}
+} BmuxmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/demuxmap.cc b/passes/techmap/demuxmap.cc
new file mode 100644
index 000000000..292b18bad
--- /dev/null
+++ b/passes/techmap/demuxmap.cc
@@ -0,0 +1,80 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2022  Marcelina Koƛcielnicka <mwk@0x04.net>
+ *
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct DemuxmapPass : public Pass {
+	DemuxmapPass() : Pass("demuxmap", "transform $demux cells to $eq + $mux cells") { }
+	void help() override
+	{
+		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+		log("\n");
+		log("    demuxmap [selection]\n");
+		log("\n");
+		log("This pass transforms $demux cells to a bunch of equality comparisons.\n");
+		log("\n");
+	}
+	void execute(std::vector<std::string> args, RTLIL::Design *design) override
+	{
+		log_header(design, "Executing DEMUXMAP pass.\n");
+
+		size_t argidx;
+		for (argidx = 1; argidx < args.size(); argidx++) {
+			break;
+		}
+		extra_args(args, argidx, design);
+
+		for (auto module : design->selected_modules())
+		for (auto cell : module->selected_cells())
+		{
+			if (cell->type != ID($demux))
+				continue;
+
+			SigSpec sel = cell->getPort(ID::S);
+			SigSpec data = cell->getPort(ID::A);
+			SigSpec out = cell->getPort(ID::Y);
+			int width = GetSize(cell->getPort(ID::A));
+
+			for (int i = 0; i < 1 << GetSize(sel); i++) {
+				if (width == 1 && data == State::S1) {
+					RTLIL::Cell *eq_cell = module->addEq(NEW_ID, sel, Const(i, GetSize(sel)), out[i]);
+					eq_cell->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
+				} else {
+					Wire *eq = module->addWire(NEW_ID);
+					RTLIL::Cell *eq_cell = module->addEq(NEW_ID, sel, Const(i, GetSize(sel)), eq);
+					eq_cell->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
+					RTLIL::Cell *mux = module->addMux(NEW_ID,
+							Const(State::S0, width),
+							data,
+							eq,
+							out.extract(i*width, width));
+					mux->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
+				}
+			}
+
+			module->remove(cell);
+		}
+	}
+} DemuxmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc
index 68f44cf6d..7d8dba439 100644
--- a/passes/techmap/simplemap.cc
+++ b/passes/techmap/simplemap.cc
@@ -299,6 +299,30 @@ void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell)
 	}
 }
 
+void simplemap_bmux(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+	SigSpec sel = cell->getPort(ID::S);
+	SigSpec data = cell->getPort(ID::A);
+	int width = GetSize(cell->getPort(ID::Y));
+
+	for (int idx = 0; idx < GetSize(sel); idx++) {
+		SigSpec new_data = module->addWire(NEW_ID, GetSize(data)/2);
+		for (int i = 0; i < GetSize(new_data); i += width) {
+			for (int k = 0; k < width; k++) {
+				RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
+				gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
+				gate->setPort(ID::A, data[i*2+k]);
+				gate->setPort(ID::B, data[i*2+width+k]);
+				gate->setPort(ID::S, sel[idx]);
+				gate->setPort(ID::Y, new_data[i+k]);
+			}
+		}
+		data = new_data;
+	}
+
+	module->connect(cell->getPort(ID::Y), data);
+}
+
 void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell)
 {
 	SigSpec lut_ctrl = cell->getPort(ID::A);
@@ -306,7 +330,6 @@ void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell)
 	lut_data.extend_u0(1 << cell->getParam(ID::WIDTH).as_int());
 
 	for (int idx = 0; GetSize(lut_data) > 1; idx++) {
-		SigSpec sig_s = lut_ctrl[idx];
 		SigSpec new_lut_data = module->addWire(NEW_ID, GetSize(lut_data)/2);
 		for (int i = 0; i < GetSize(lut_data); i += 2) {
 			RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
@@ -400,6 +423,7 @@ void simplemap_get_mappers(dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)>
 	mappers[ID($nex)]         = simplemap_eqne;
 	mappers[ID($mux)]         = simplemap_mux;
 	mappers[ID($tribuf)]      = simplemap_tribuf;
+	mappers[ID($bmux)]        = simplemap_bmux;
 	mappers[ID($lut)]         = simplemap_lut;
 	mappers[ID($sop)]         = simplemap_sop;
 	mappers[ID($slice)]       = simplemap_slice;
diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc
index 4e437e409..e21ec452c 100644
--- a/passes/tests/test_cell.cc
+++ b/passes/tests/test_cell.cc
@@ -69,6 +69,48 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
 		cell->setPort(ID::Y, wire);
 	}
 
+	if (cell_type == ID($bmux))
+	{
+		int width = 1 + xorshift32(8);
+		int swidth = 1 + xorshift32(4);
+
+		wire = module->addWire(ID::A);
+		wire->width = width << swidth;
+		wire->port_input = true;
+		cell->setPort(ID::A, wire);
+
+		wire = module->addWire(ID::S);
+		wire->width = swidth;
+		wire->port_input = true;
+		cell->setPort(ID::S, wire);
+
+		wire = module->addWire(ID::Y);
+		wire->width = width;
+		wire->port_output = true;
+		cell->setPort(ID::Y, wire);
+	}
+
+	if (cell_type == ID($demux))
+	{
+		int width = 1 + xorshift32(8);
+		int swidth = 1 + xorshift32(6);
+
+		wire = module->addWire(ID::A);
+		wire->width = width;
+		wire->port_input = true;
+		cell->setPort(ID::A, wire);
+
+		wire = module->addWire(ID::S);
+		wire->width = swidth;
+		wire->port_input = true;
+		cell->setPort(ID::S, wire);
+
+		wire = module->addWire(ID::Y);
+		wire->width = width << swidth;
+		wire->port_output = true;
+		cell->setPort(ID::Y, wire);
+	}
+
 	if (cell_type == ID($fa))
 	{
 		int width = 1 + xorshift32(8);
@@ -855,8 +897,10 @@ struct TestCellPass : public Pass {
 		cell_types[ID($logic_and)] = "ABSY";
 		cell_types[ID($logic_or)]  = "ABSY";
 
+		cell_types[ID($mux)] = "*";
+		cell_types[ID($bmux)] = "*";
+		cell_types[ID($demux)] = "*";
 		if (edges) {
-			cell_types[ID($mux)] = "*";
 			cell_types[ID($pmux)] = "*";
 		}
 
diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v
index e9129f690..b14488ff4 100644
--- a/techlibs/common/simlib.v
+++ b/techlibs/common/simlib.v
@@ -1292,6 +1292,33 @@ endmodule
 
 // --------------------------------------------------------
 
+module \$bmux (A, S, Y);
+
+parameter WIDTH = 0;
+parameter S_WIDTH = 0;
+
+input [(WIDTH << S_WIDTH)-1:0] A;
+input [S_WIDTH-1:0] S;
+output [WIDTH-1:0] Y;
+
+wire [WIDTH-1:0] bm0_out, bm1_out;
+
+generate
+	if (S_WIDTH > 1) begin:muxlogic
+		\$bmux #(.WIDTH(WIDTH), .S_WIDTH(S_WIDTH-1)) bm0 (.A(A), .S(S[S_WIDTH-2:0]), .Y(bm0_out));
+		\$bmux #(.WIDTH(WIDTH), .S_WIDTH(S_WIDTH-1)) bm1 (.A(A[(WIDTH << S_WIDTH)-1:WIDTH << (S_WIDTH - 1)]), .S(S[S_WIDTH-2:0]), .Y(bm1_out));
+		assign Y = S[S_WIDTH-1] ? bm1_out : bm0_out;
+	end else if (S_WIDTH == 1) begin:simple
+		assign Y = S ? A[1] : A[0];
+	end else begin:passthru
+		assign Y = A;
+	end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
 module \$pmux (A, B, S, Y);
 
 parameter WIDTH = 0;
@@ -1317,6 +1344,26 @@ end
 
 endmodule
 
+// --------------------------------------------------------
+
+module \$demux (A, S, Y);
+
+parameter WIDTH = 1;
+parameter S_WIDTH = 1;
+
+input [WIDTH-1:0] A;
+input [S_WIDTH-1:0] S;
+output [(WIDTH << S_WIDTH)-1:0] Y;
+
+genvar i;
+generate
+	for (i = 0; i < (1 << S_WIDTH); i = i + 1) begin:slices
+		assign Y[i*WIDTH+:WIDTH] = (S == i) ? A : 0;
+	end
+endgenerate
+
+endmodule
+
 // --------------------------------------------------------
 `ifndef SIMLIB_NOLUT
 
@@ -1326,30 +1373,9 @@ parameter WIDTH = 0;
 parameter LUT = 0;
 
 input [WIDTH-1:0] A;
-output reg Y;
-
-wire lut0_out, lut1_out;
+output Y;
 
-generate
-	if (WIDTH <= 1) begin:simple
-		assign {lut1_out, lut0_out} = LUT;
-	end else begin:complex
-		\$lut #( .WIDTH(WIDTH-1), .LUT(LUT                  ) ) lut0 ( .A(A[WIDTH-2:0]), .Y(lut0_out) );
-		\$lut #( .WIDTH(WIDTH-1), .LUT(LUT >> (2**(WIDTH-1))) ) lut1 ( .A(A[WIDTH-2:0]), .Y(lut1_out) );
-	end
-
-	if (WIDTH > 0) begin:lutlogic
-		always @* begin
-			casez ({A[WIDTH-1], lut0_out, lut1_out})
-				3'b?11: Y = 1'b1;
-				3'b?00: Y = 1'b0;
-				3'b0??: Y = lut0_out;
-				3'b1??: Y = lut1_out;
-				default: Y = 1'bx;
-			endcase
-		end
-	end
-endgenerate
+\$bmux #(.WIDTH(1), .S_WIDTH(WIDTH)) mux(.A(LUT), .S(A), .Y(Y));
 
 endmodule
 
diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v
index 667773e1b..91d385b80 100644
--- a/techlibs/common/techmap.v
+++ b/techlibs/common/techmap.v
@@ -59,7 +59,7 @@ module _90_simplemap_compare_ops;
 endmodule
 
 (* techmap_simplemap *)
-(* techmap_celltype = "$pos $slice $concat $mux $tribuf" *)
+(* techmap_celltype = "$pos $slice $concat $mux $tribuf $bmux" *)
 module _90_simplemap_various;
 endmodule
 
@@ -597,6 +597,43 @@ module _90_pmux (A, B, S, Y);
 	assign Y = |S ? Y_B : A;
 endmodule
 
+// --------------------------------------------------------
+// Demultiplexers
+// --------------------------------------------------------
+
+(* techmap_celltype = "$demux" *)
+module _90_demux (A, S, Y);
+	parameter WIDTH = 1;
+	parameter S_WIDTH = 1;
+
+	(* force_downto *)
+	input [WIDTH-1:0] A;
+	(* force_downto *)
+	input [S_WIDTH-1:0] S;
+	(* force_downto *)
+	output [(WIDTH << S_WIDTH)-1:0] Y;
+
+	generate
+		if (S_WIDTH == 0) begin
+			assign Y = A;
+		end else if (S_WIDTH == 1) begin
+			assign Y[0+:WIDTH] = S ? 0 : A;
+			assign Y[WIDTH+:WIDTH] = S ? A : 0;
+		end else begin
+			localparam SPLIT = S_WIDTH / 2;
+			wire [(1 << (S_WIDTH-SPLIT))-1:0] YH;
+			wire [(1 << SPLIT)-1:0] YL;
+			$demux #(.WIDTH(1), .S_WIDTH(SPLIT)) lo (.A(1'b1), .S(S[SPLIT-1:0]), .Y(YL));
+			$demux #(.WIDTH(1), .S_WIDTH(S_WIDTH-SPLIT)) hi (.A(1'b1), .S(S[S_WIDTH-1:SPLIT]), .Y(YH));
+			genvar i;
+			for (i = 0; i < (1 << S_WIDTH); i = i + 1) begin
+				localparam [S_WIDTH-1:0] IDX = i;
+				assign Y[i*WIDTH+:WIDTH] = (YL[IDX[SPLIT-1:0]] & YH[IDX[S_WIDTH-1:SPLIT]]) ? A : 0;
+			end
+		end
+	endgenerate
+endmodule
+
 
 // --------------------------------------------------------
 // LUTs
-- 
cgit v1.2.3