Question

Could we have any syntax where case statement is scalable? Let me explain with an example: Mux

If there were only 2 select lines

always @(A[1:0]) begin
 case (A[1:0])
  2'b00 : select = 4'b1110;
  2'b01 : select = 4'b1101;
  2'b10 : select = 4'b1011;
  2'b11 : select = 4'b0111;
 endcase
end 

For 3 select lines

always @(A[2:0]) begin
 case (A[2:0])
  3'b000 : select = 8'b1111_1110;
  3'b001 : select = 8'b1111_1101;
  3'b010 : select = 8'b1111_1011;
  3'b011 : select = 8'b1111_0111;
  3'b100 : select = 8'b1110_1111;
  3'b101 : select = 8'b1101_1111;
  3'b110 : select = 8'b1011_1111;
  3'b111 : select = 8'b0111_1111;
 endcase
end 

My questions:

  • Is there a generic way of writing code that could address a mux with any number of select lines? 2,3,4...

  • Is there any other way of achieving this using syntax other than case statement?

Any feedback is welcome. regards

Was it helpful?

Solution

If it is the walking 0's pattern that your after how about:

  localparam SHIFT_W = 3;
  localparam OUT_W   = 2**SHIFT_W;
  reg [SHIFT_W-1:0] shift;
  reg   [OUT_W-1:0] out;

  always_comb begin
    out = ~(OUT_W'(1'b1 << shift));
  end

As suggested by nguthrie. Shift to create a walking 1, then invert to create a walking 0.


My original suggestion (which was a bit verbose) using SystemVerilog to directly create a walking 0:

  localparam SHIFT_W = 3;
  localparam OUT_W   = 2**SHIFT_W;
  reg [SHIFT_W-1:0] shift;
  reg   [OUT_W-1:0] out;
  always_comb begin
    out = OUT_W'( $signed{ 1'b1, 1'b0, {OUT_W{1'b1}} }) >>> (OUT_W-shift) );
  end

WIDTH`() Casts to the correct width to stop LHS RHS width mismatch warnings. $signed() Casts to a signed number to allow >>> to shift by sign extending. This could also be written as:

out = OUT_W'( { {OUT_W{1'b1}}, 1'b0, {OUT_W{1'b1}} } >> (OUT_W-shift) );

For Verilog-2001, you will just get LHS RHS width mismatch warnings:

out = { {OUT_W{1'b1}}, 1'b0, {OUT_W{1'b1}} } >> (OUT_W-shift);

Which has removed the need to sign extend during shift.

OTHER TIPS

Since the given criteria is for a binary to one-cold decoder, a for-loop can be used. It typicality gives better timing and area for synthesize compared to a bit-shifter; from my experience. You should do your own comparison as it depends your standard cell library and how well your synthesizer can optimize.

module bin2cold #(parameter WIDTH=4) (
  output reg [2**WIDTH-1:0] cold,
  input      [   WIDTH-1:0] bin );

integer idx;
always @* begin
  for(idx = 0; idx < 2**WIDTH; idx=idx+1) begin
    cold[idx] = (bin != idx);
  end
end

endmodule

This will give you a succinct generic mux, where all inputs are aggregated in a vector called in, and then they get separated in input_array:

module mux #(parameter  DW  = 32,
            parameter   N   = 2) 
(

 input   [(DW*N)-1:0]           in,
 input   [$clog2(N)-1:0]    sel,   

 output  logic [DW-1:0]       out
);

genvar ig;

logic    [DW-1:0] input_array [0:N-1];

assign  out = input_array[sel];

//separating inputs
generate
    for(ig=0; ig<N; ig=ig+1) begin: array_assignments
        assign  input_array[ig] = in[(ig*DW)+:DW];
    end
endgenerate

endmodule

What you want is a bit different in that, the value of in is already known and can internally be generated. Therefore, the generate/endgenerate block in my code, can be replaced by one of the methods described by others.

    module mux #(parameter  DW  = 32,
            parameter   N   = 2) 
(

 input   [(DW*N)-1:0]           in, //not used anymore
 input   [$clog2(N)-1:0]    sel,   

 output  logic [DW-1:0]       out
);

genvar ig;

logic    [DW-1:0] input_array [0:N-1];

assign  out = input_array[sel];

generate
    for(ig=0; ig<N; ig=ig+1) begin: array_assignments
        assign  input_array[ig] = ~( 2**ig );
    end
endgenerate

endmodule

(not sure of this is purely Verilog or SystemVerilog)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top