How get the bit width of a value at Verilog compile time

J

James Harris

Guest
Warning, newbie question ahead. Give the following which I've put at
the top of a Verilog module

parameter BITS = 32;

I need a counter to count down from BITS to zero (inclusive) so the
counter needs to be something like floor(log2(BITS)) + 1 wide. For
example, if the parameter is 32 the counter needs to be 6 bits wide
and if the parameter is 8 the counter needs to be 4 bits wide. I tried
defining the width of the counter as

reg[log2(BITS) + 1] counter;

but this fails. There seems to be no way to take log2 - or any other
type of log - at compile time. I've ended up with

parameter LOG2BITS = 4;
parameter BITS = 2 ** LOG2BITS;
...
reg[LOG2BITS + 1] counter;

where LOG2BITS is set first then BITS is set based on the value of
LOG2BITS.

Is there a better way of doing this?

James
 
On 28 Sep, 17:25, James Harris <james.harri...@googlemail.com> wrote:

Warning, newbie question ahead. Give the following which I've put at
Oops: "Give" should be "Given".

James
 
On Sep 28, 5:25 pm, James Harris wrote:
Warning, newbie question ahead.
Not newbie, but a very common need.

counter needs to be something like
floor(log2(BITS)) + 1 wide.
There's a system function $clog2 that should do the job. Check
its behaviour carefully: it really does return the ceiling of
log2, so it isn't quite the same as "bits-to-fit-this-integer",
which you can get by $clog2(N+1). $clog2 is a constant function
if given a constant argument, so it should be OK to use the
result as if it were a parameter (i.e. use it in things
like vector width declarations).

However, support for $clog2 and constant functions is
not universal in current tools, so you may have to resort
to other grimness. If the width is reasonably limited,
you can use a macro definition:

`define BITS_TO_FIT(N) ( \
N < 2 ? 1 : \
N < 4 ? 2 : \
N < 8 ? 3 : \
N < 16 ? 4 : \
N < 32 ? 5 : \
N < 64 ? 6 : \
7 )

parameter SIZE = whatever;
localparam COUNTWIDTH = `BITS_TO_FIT(SIZE);

OK, OK, don't shout at me - I never promised it
was going to be nice...
--
Jonathan Bromley
 
On Tue, 28 Sep 2010 14:48:19 -0700 (PDT), gabor wrote:

I don't remember where I swiped this code:
[...]
function [31:0] log2;
input [31:0] value;
[...]
I think it started as a recursive function that completely
hosed XST synthesis, and then was re-written as a loop.
Right, that's the way I've done it in VHDL for years;
but in Verilog it's still exposed to the problem of
whether tools really understand what a "constant
function" is (i.e. the result can be used to set a
parameter or to size a vector).

Since it's so much cleaner than the macro I posted,
it would probably be a good idea to start with this
and then fall back to the macro hack if your synthesis
tool doesn't like the function.
--
Jonathan Bromley
 
On 9/28/2010 3:28 PM, Jonathan Bromley wrote:

Since it's so much cleaner than the macro I posted,
it would probably be a good idea to start with this
and then fall back to the macro hack if your synthesis
tool doesn't like the function.
Or us $clog2() like you mentioned earlier. FYI Icarus
Verilog does not support constant user functions, but
it does support $clog2() in a constant context. For
maximum portability you probably need to have an ifdef
that switches between $clog2() or the constant user
function. Some of the name brand simulators do not
support $clog2(), but their synthesis does! I think
there is a clog2b function out there that matches what
$clog2() does so you can easily just switch between
the two version using a define.

Cary
 
On Sep 28, 12:51 pm, Jonathan Bromley <s...@oxfordbromley.plus.com>
wrote:
On Sep 28, 5:25 pm, James Harris wrote:

Warning, newbie question ahead.

Not newbie, but a very common need.

counter needs to be something like
  floor(log2(BITS)) + 1 wide.

There's a system function $clog2 that should do the job.  Check
its behaviour carefully: it really does return the ceiling of
log2, so it isn't quite the same as "bits-to-fit-this-integer",
which you can get by $clog2(N+1).  $clog2 is a constant function
if given a constant argument, so it should be OK to use the
result as if it were a parameter (i.e. use it in things
like vector width declarations).

However, support for $clog2 and constant functions is
not universal in current tools, so you may have to resort
to other grimness.  If the width is reasonably limited,
you can use a macro definition:

  `define BITS_TO_FIT(N) ( \
     N <  2 ? 1 : \
     N <  4 ? 2 : \
     N <  8 ? 3 : \
     N < 16 ? 4 : \
     N < 32 ? 5 : \
     N < 64 ? 6 : \
               7 )

  parameter SIZE = whatever;
  localparam COUNTWIDTH = `BITS_TO_FIT(SIZE);

OK, OK, don't shout at me - I never promised it
was going to be nice...
--
Jonathan Bromley
I don't remember where I swiped this code:

`timescale 1 ns / 1 ps
module test ();


function [31:0] log2;
input [31:0] value;
integer i;
reg [31:0] j;
begin
j = value - 1;
log2 = 0;
for (i = 0; i < 31; i = i + 1)
if (j) log2 = i+1;
end
endfunction

initial
begin
$display("10 = %d", log2(10));
$display("32 = %d", log2(32));
$display("64 = %d", log2(64));
$display("4 = %d", log2(4));
end
endmodule

I think it started as a recursive function that completely
hosed XST synthesis, and then was re-written as a loop.

Regards,
Gabor
 
FYI, we use a very similar constant function, and have
used with many synthesis tools without much issue -
XST, Precision, dc, Quartus.

A few had issues with directly calling the log2 function
during a module instanciation - we had to create an intermediate
variable. i.e.:

foo #( .some_param( foo_param ), .log2_some_param( log2( foo_param ) ) foo();

Would fail. Creating a localparam would workaround:

localparam log2_foo_param = log2( foo_param );
foo #( .some_param( foo_param ), .log2_some_param( log2_foo_param ) ) foo();

Probably more than the OP wanted to know....

--Mark



In article <3lq4a65r8r001ho501hpf2qvcm71f87r8f@4ax.com>,
Jonathan Bromley <spam@oxfordbromley.plus.com> wrote:
On Tue, 28 Sep 2010 14:48:19 -0700 (PDT), gabor wrote:

I don't remember where I swiped this code:
[...]
function [31:0] log2;
input [31:0] value;
[...]
I think it started as a recursive function that completely
hosed XST synthesis, and then was re-written as a loop.

Right, that's the way I've done it in VHDL for years;
but in Verilog it's still exposed to the problem of
whether tools really understand what a "constant
function" is (i.e. the result can be used to set a
parameter or to size a vector).

Since it's so much cleaner than the macro I posted,
it would probably be a good idea to start with this
and then fall back to the macro hack if your synthesis
tool doesn't like the function.
--
Jonathan Bromley
 
On 28 Sep, 17:51, Jonathan Bromley <s...@oxfordbromley.plus.com>
wrote:
On Sep 28, 5:25 pm, James Harris wrote:

Warning, newbie question ahead.

Not newbie, but a very common need.

counter needs to be something like
  floor(log2(BITS)) + 1 wide.

There's a system function $clog2 that should do the job.  Check
its behaviour carefully: it really does return the ceiling of
log2, so it isn't quite the same as "bits-to-fit-this-integer",
which you can get by $clog2(N+1).  $clog2 is a constant function
if given a constant argument, so it should be OK to use the
result as if it were a parameter (i.e. use it in things
like vector width declarations).

However, support for $clog2 and constant functions is
not universal in current tools, so you may have to resort
to other grimness.  If the width is reasonably limited,
you can use a macro definition:

  `define BITS_TO_FIT(N) ( \
     N <  2 ? 1 : \
     N <  4 ? 2 : \
     N <  8 ? 3 : \
     N < 16 ? 4 : \
     N < 32 ? 5 : \
     N < 64 ? 6 : \
               7 )

  parameter SIZE = whatever;
  localparam COUNTWIDTH = `BITS_TO_FIT(SIZE);

OK, OK, don't shout at me - I never promised it
was going to be nice...
No, that's great, thanks. $clog2 is supported by Icarus (but not by my
current Xilinx installation - I'll have to do something different
there but then the whole installation seems a bit odd so I'll come
back to it). I also wanted but didn't know about localparam so that's
a help too!

James
 

Welcome to EDABoard.com

Sponsor

Back
Top