DrDean
DrDean

Reputation: 127

Generate If Statements in Verilog

I'm trying to create a synthesizable, parametrized priority encoder in Verilog. Specifically, I want to find the least significant 1 in a vector and return a vector containing just that 1. For example:

IN[3:0] | OUT[4:0]
--------+---------
1010    | 00010
1111    | 00001
0100    | 00100
0000    | 10000   (special case)

So if the vectors are four bits wide, the code is:

if (in[0]==1'b1) least_one = 1;
else if (in[1]==1'b1) least_one = 2;
else if (in[2]==1'b1) least_one = 4;
else if (in[3]==1'b1) least_one = 8;
else out = 16; // special case in==0, set carry bit

I need a general, scalable way to do this because the input/output vector length is parametrized. My current code is:

module least_one_onehot
#(parameter ADDR_WIDTH=4)
(output reg [ADDR_WIDTH:0] least_one,
input [ADDR_WIDTH-1:0] in);

genvar i;

always @(in) begin
    if (in[0]==1'b1) least_one = 1;
    generate for (i=1; i<ADDR_WIDTH; i=i+1) begin : U 
        else if (in[i]==1'b1) least_one = 2**i; 
        end 
        endgenerate
    else least_one = 2**ADDR_WIDTH;
    end

endmodule

When I try to compile this, I receive the following errors:

file: least_one_onehot.v
        generate for (i=1; i<ADDR_WIDTH; i=i+1) begin : U
               |
ncvlog: *E,GIWSCP (least_one_onehot.v,10|8): Generated instantiation can only be valid within a module scope [12.1.3(IEEE 2001)].
                        else if (in[i]==1'b1) least_one = 2**i; 
                           |
ncvlog: *E,NOTSTT (least_one_onehot.v,11|6): expecting a statement [9(IEEE)].
                endgenerate
                          |
ncvlog: *E,GIWSCP (least_one_onehot.v,13|12): Generated instantiation can only be valid within a module scope [12.1.3(IEEE 2001)].
                else least_one = 2**ADDR_WIDTH;
                   |
ncvlog: *E,NOTSTT (least_one_onehot.v,14|5): expecting a statement [9(IEEE)]

I've tried various arrangements of the generate, if, and always statements, all without success. Anyone know the proper syntax for this? Case-statement implementation or other alternatives would also be fine. Thanks.

Upvotes: 3

Views: 31077

Answers (4)

jelman
jelman

Reputation: 123

Personally, I like the following block of code for what you need: assign out = {1'b1,in} & ((~{1'b1,in})+1);

You could try this (dropping the extra high bit for legibility), but I like to explicitly do the twos compliment to avoid any potential compatibility problems.

assign out = in & (-1*in);

Upvotes: 1

toolic
toolic

Reputation: 62236

This simulates the way you want it to, but it is not synthesizable (you didn't specify if that was a requirement):

module least_one_onehot #(parameter ADDR_WIDTH=4) (
    output reg [ADDR_WIDTH-1:0] least_one,
    input      [ADDR_WIDTH-1:0] in
);

always @* begin
    least_one = '0;
    for (int i=ADDR_WIDTH-1; i>=0; i--) begin
        if (in[i]) least_one = 2**i;
    end
end

endmodule

Note that it uses SystemVerilog constructs.

Upvotes: 1

Greg
Greg

Reputation: 19112

You do not need a generate block. You could use:

integer i;
reg found;
always @(in) begin
  least_one = {(ADDR_WIDTH+1){1'b0}};
  found = 1'b0;
  for (i=0; i<ADDR_WIDTH; i=i+1) begin
    if (in[i]==1'b1 && found==1'b0) begin
      least_one[i] = 1'b1;
      found = 1'b1;
    end
  end
  least_one[ADDR_WIDTH] = (found==1'b0);
end

If you really want to use a generate block, then you need to assign each bit.

assign least_one[0] = in[0];
assign least_one[ADDR_WIDTH] = (in == {ADDR_WIDTH{1'b0}});
genvar i;
generate
  for (i=1; i<ADDR_WIDTH; i=i+1) begin : U
    assign least_one[i] = in[i] && (in[i - 1:0] == {i{1'b0}});
  end
endgenerate

Upvotes: 1

Brian Magnuson
Brian Magnuson

Reputation: 1487

I think you misunderstand how generate works. It isn't a text pre-processor that emits the code in between the generate/endgenerate pair with appropriate substitutions. You have to have complete syntactic entities withing the pair. I don't have access to a simulator right this minute but this might do the trick for you (totally untested)

genvar i;
generate
    for (i = 1; i < ADDR_WIDTH; i = i + 1) begin : U
        least_one[i] = in[i] & ~|in[i - 1:0];
    end
endgenerate
least_one[0] = in[0];
least_one[ADDR_WIDTH] = ~|in;

Ordinarily Verilog would complain about the non-constant bit slice width but since it's within a generate loop it might work.

Failing something like the above you just test for the first set bit in a for-loop and then decode that result.

Upvotes: 2

Related Questions