Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions passes/proc/proc_mux.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<dict<SigBit, int>> 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;
Expand Down
50 changes: 50 additions & 0 deletions tests/proc/proc_mux_dominant.v
Original file line number Diff line number Diff line change
@@ -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
40 changes: 40 additions & 0 deletions tests/proc/proc_mux_dominant.ys
Original file line number Diff line number Diff line change
@@ -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
Loading