Mux/De-Mux/Case Statements in SystemVerilog :

Multiplexers are used to select a single input from several inputs with the help of Select signal. Muxes form a combinational logic that can be written as follows. The number of bits required of select are calculated as 2^n = number of inputs , where n is number of select bits.

logic [3:0] select;
logic output, input;
always_comb begin
case (select[3:0]) begin
4'b0001 : output = input_1;
4'b0010 : output = input_2;
4'b0100 : output = input_3;
4'b1000 : output = input_4;
default : output = 1'b0;
endcase
end

The above logic can also be coded as using “if else” statement using a always_comb or using an assign statement using “? :” operator. In this case Mux becomes a Priority Encoded as priority of input_1 > input_2 > input_3 > input_4.

always_comb begin
if(select == 4'b0001) output = input_1;
else if (select == 4'b0001) output = input_1;
else if (select == 4'b0010) output = input_2;
else if (select == 4'b0100) output = input_3;
else if (select == 4'b1000) output = input_4;
else output = 1'b0;
end

assign output = (select == 4'b0001) ? input_1 :
(select == 4'b0010) ? input_2 :
(select == 4'b0100) ? input_2 :
(select == 4'b1000) ? input_2 :
1'b0;

If the Muxes are being used to drive Data Bus then its recommended that Selects to be driven from Flops (For E.x FSM ) otherwise the outputs may change continuously if the selects are not stable in that cycle. Also, if the select to the Muxes is not from a Flop its recommended to initialize select signal first as it avoids X-propagation into downstream logic:

always_comb begin
select = 4'b0;
case (select[3:0]) begin
4'b0001 : output = input_1;
4'b0010 : output = input_2;
4'b0100 : output = input_3;
4'b1000 : output = input_4;
default : output = 1'b0;
endcase
end

De-Multiplexers or decoders perform opposite operation to Multiplexers. Here a single input is distributed to many outputs depending on the select. The number is bits in select can be calculated as power of 2 of number of outputs. The decoders are often used in converting packed signals to unpacked signals or per entry enable for Arrays.

logic [1:0]  sel;
logic [3:0] out;

always_comb begin
unique case (sel[1:0]) begin
2'b00 : out = 4'b0001;
2'b01 : out = 4'b0010;
2'b10 : out = 4'b0100;
2'b11 : out = 4'b1000;
endcase
end

Mux and De-Mux design in SV is often done using Case Statements. There are different flavors of case statements and each one is used in different scenarios to achieve different results.

Casex : In this type of case statement bits used in comparison can be selectively ignored if the values of comparison are ‘x’ or ‘z’. casex statements can result in different simulation and synthesis results so one needs to be extra careful will using casex. For E.x : When select[2:0] is 3’bxxx the output in simulation could be 2’b01 while in synthesis output could be 2’b11 because select[2:0] becomes 3’b001 in ‘x’ or ‘z’ conditions.

logic [2:0] select;
logic [1:0] output_a;

always_comb begin
casex (select[2:0])
3'bxx1 : output_a = 2'b01;
3'b01x : output_a = 2'b10;
3'b001 : output_a = 2'b11;
3'b100 : output_a = 2'b00;
default : output_a = 2'b00;
end

Casez : In casez statements, bits with ‘z’ values are ignored or treated as don’t-care. However, the bits with ‘x’ values are used in comparison. The casez statements are very useful in creating a priority logic and are more readable than if-else statements.

logic [2:0] selb;
logic [1:0] output_b;

// Priority of selection [0] > [1] > [2]
always_comb begin
casez (selb[2:0])
3'b??1 : output_b = 2'b01;
3'b?10 : output_b = 2'b10;
3'b100 : output_b = 2'b11;
default : output_b = 2'b00;
end

Synthesis Directives : In some instances depending on the compiler it is allowed to pass on compiler directives such as ‘// synthesis full_case‘ and ‘// synthesis parallel_case‘ with the case statement.

logic [2:0] selb;
logic [1:0] output_b;

// 1) Example of full case
// Priority of selection [0] > [1] > [2]
always_comb begin
casez (selb[2:0]) // synthesis full_case
3'b??1 : output_b = 2'b01;
3'b?10 : output_b = 2'b10;
3'b100 : output_b = 2'b11;
end

In full_case, the user explicitly tells synthesis that we do not care about output when the selb = 2’b00. The Mux in this case infers storage and generates less hardware by not evaluating all the possible selb values.

logic [2:0] selc;
logic input_x, input_y, input_w, output_z;
always_comb begin
case (selc) // synthesis parallel_case
3'b001 : output_z = input_x;
3'b010 : output_z = input_y;
2'b100 : output_z = input_w;
endcase
end

In parallel case, user implies that the selection conditions are mutually exclusive so the synthesis generates limited States. Parallel case is often useful in case of one-hot logic for E.g FSM.

In general compiler directives should be avoided as it may result in different Simulation and Synthesis results.

Basic Gates using Muxes :

  • Inverter/NOT Gate using 2:1 Mux
logic input_x;
logic output_y;
assign ouput_y = input_x ? 1'b0 : 1'b1;
  • AND Gate using 2:1 Mux
logic output_y;
logic input_x;
logic input_w;
assign ouput_y = input_x ? input_w : 1'b0;
  • OR Gate using 2:1 Mux
logic output_y;
logic input_x;
logic input_w;
assign ouput_y = input_x ? 1'b1 : input_w;
  • XOR Gate using 2:1 Mux
logic output_y;
logic input_x;
logic input_w;
assign ouput_y = input_x ? ~input_w : input_w;