diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 68127d1bc..b60b4fff2 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -393,6 +393,27 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d if (full_case_bits.count(sig[i])) result[i] = State::Sx; + // For full_case switches, if a majority of arms assign the same value to a bit + // (via direct actions), use that as the else-branch seed instead of Sx. This + // lets proc_mux skip generating mux cells for arms that produce the dominant value. + if (!full_case_bits.empty() && !sw->cases.empty()) { + int n = GetSize(sw->cases); + std::vector> counts(GetSize(sig)); + for (auto cs2 : sw->cases) { + RTLIL::SigSpec probe = RTLIL::SigSpec(RTLIL::State::Sx, sig.size()); + for (auto &action : cs2->actions) + sig.replace(action.first, action.second, &probe); + for (int i = 0; i < GetSize(sig); i++) + if (full_case_bits.count(sig[i]) && probe[i] != RTLIL::State::Sx) + counts[i][probe[i]]++; + } + for (int i = 0; i < GetSize(sig); i++) { + if (!full_case_bits.count(sig[i])) continue; + for (auto &kv : counts[i]) + if (kv.second * 2 > n) { result[i] = kv.first; break; } + } + } + // evaluate in reverse order to give the first entry the top priority RTLIL::SigSpec initial_val = result; RTLIL::Cell *last_mux_cell = NULL; diff --git a/tests/proc/proc_mux_dominant.v b/tests/proc/proc_mux_dominant.v new file mode 100644 index 000000000..1e02533b2 --- /dev/null +++ b/tests/proc/proc_mux_dominant.v @@ -0,0 +1,50 @@ +// Test cases for proc_mux dominant-value optimization. +// +// When a full_case switch has a majority of arms assigning the same value to a +// signal bit, proc_mux uses that dominant value as the starting point instead +// of Sx. Arms that produce the dominant value are then skipped (the mux +// condition evaluates to "when == else"), avoiding spurious $eq/$mux cells. + +// dominant_explicit: 3 of 4 arms assign the same constants (dominant values). +// Expected after proc: one $mux per output word, one $logic_not for the +// selector — zero $eq cells, zero $pmux cells. +module dominant_explicit(input [1:0] s, output reg [2:0] y, output reg [1:0] z); + always @* begin + y = 3'b001; + z = 2'b00; + case (s) + 2'b00: begin y = 3'b110; z = 2'b11; end // only arm that differs + 2'b01: begin y = 3'b001; z = 2'b00; end // explicit dominant + 2'b10: begin y = 3'b001; z = 2'b00; end // explicit dominant + 2'b11: begin y = 3'b001; z = 2'b00; end // explicit dominant + endcase + end +endmodule + +// dominant_wire: dominant value is an input wire (not a constant). +// Expected after proc: 1 $logic_not + 1 $mux, no $eq/$pmux. +module dominant_wire(input [1:0] s, input [2:0] a, output reg [2:0] y); + always @* begin + y = a; + case (s) + 2'b00: y = 3'b110; // only arm that differs + 2'b01: y = a; // explicit dominant + 2'b10: y = a; // explicit dominant + 2'b11: y = a; // explicit dominant + endcase + end +endmodule + +// no_dominant: all four arms assign distinct values — no majority. +// The optimization must NOT fire; behavior must be unchanged. +// Expected after proc: $eq cells for each non-zero compare arm, $pmux. +module no_dominant(input [1:0] s, input [2:0] a, b, c, d, output reg [2:0] y); + always @* begin + case (s) + 2'b00: y = a; + 2'b01: y = b; + 2'b10: y = c; + 2'b11: y = d; + endcase + end +endmodule diff --git a/tests/proc/proc_mux_dominant.ys b/tests/proc/proc_mux_dominant.ys new file mode 100644 index 000000000..7cdce9178 --- /dev/null +++ b/tests/proc/proc_mux_dominant.ys @@ -0,0 +1,40 @@ +# Test that proc_mux uses a dominant-value pre-scan to avoid generating +# unnecessary mux cells when a full_case switch has a majority of arms +# assigning the same value. + +# 3 of 4 arms assign identical constants for both outputs. +# The optimization should seed the result with the dominant values (3'b001, +# 2'b00) so only the one differing arm (2'b00 -> 3'b110, 2'b11) generates +# cells. Each output word is a separate SigSnippet, so we expect one +# $logic_not + one $mux per word = 2 of each total. +read_verilog proc_mux_dominant.v +hierarchy -top dominant_explicit +proc +select -assert-count 2 t:$logic_not +select -assert-count 2 t:$mux +select -assert-count 0 t:$eq +select -assert-count 0 t:$pmux + +# 3 of 4 arms pass through an input wire 'a'. The dominant value is a wire +# signal rather than a constant; the optimization must still recognise it. +# Only one arm differs (2'b00 -> 3'b110), producing 1 $logic_not + 1 $mux. +design -reset +read_verilog proc_mux_dominant.v +hierarchy -top dominant_wire +proc +select -assert-count 1 t:$logic_not +select -assert-count 1 t:$mux +select -assert-count 0 t:$eq +select -assert-count 0 t:$pmux + +# All four arms assign distinct values; no majority exists. The optimization +# must not fire and the generated netlist must be functionally correct. +design -reset +read_verilog proc_mux_dominant.v +hierarchy -top no_dominant +proc +# Three explicit non-zero compare arms each produce an $eq; the 2'b00 arm +# uses $logic_not (all-zero check); all results are merged into one $pmux. +select -assert-count 3 t:$eq +select -assert-count 1 t:$logic_not +select -assert-count 1 t:$pmux