3

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.

2
  • There's a difference between procedural contexts and module contexts. See this answer. Commented Feb 26, 2013 at 3:34
  • Out should be one-bit wider than in to accommodate all-zero case. I've clarified the original post. Commented Feb 26, 2013 at 14:38

4 Answers 4

2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

You need to prefix assign to all the least_one[N] = statements. Otherwise, your code looks like it will compile correctly.
@Brian Magnuson: One inefficiency in this code (that becomes apparent when ADDR_WIDTH becomes large) is that its size is O(N log N) where N=ADDR_WIDTH. The calculation of least_one can be very easily formulated as a parallel prefix computation which makes the size of the design O(N). Basically, it exploits the fact that the terms |in[i-1:0] have much logic in common.
When I face a problem like this (especially when the bit width is large), where it is hard to write the parallel prefix computation logic in a generate statement, I use a (parameterized) Perl script to generate the SystemVerilog code and invoke it in a Makefile before I compile for simulation or synthesis.
1

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

1 Comment

True, I don't require a generate statement. Anything synthesizable that gets the job done is fine.
1

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.

3 Comments

According to the original code (not shown in the truth table), if in is all zeros then the MSB of least_one needs to be high. Assign the bit before the for-loop and correct your least_one range.
@Greg: I already posted a comment to the OP to clarify the outputs.
Yes, code needs to be synthesizable. I'd added this requirement to the original post.
1

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);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.