Discussion:
How get the bit width of a value at Verilog compile time
(too old to reply)
James Harris
2010-09-28 16:25:07 UTC
Permalink
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
James Harris
2010-09-28 16:29:01 UTC
Permalink
Post by James Harris
Warning, newbie question ahead. Give the following which I've put at
Oops: "Give" should be "Given".

James
Jonathan Bromley
2010-09-28 16:51:34 UTC
Permalink
Post by James Harris
Warning, newbie question ahead.
Not newbie, but a very common need.
Post by James Harris
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
gabor
2010-09-28 21:48:19 UTC
Permalink
Post by Jonathan Bromley
Post by James Harris
Warning, newbie question ahead.
Not newbie, but a very common need.
Post by James Harris
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,
  `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[i]) 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
Jonathan Bromley
2010-09-28 22:28:30 UTC
Permalink
[...]
Post by gabor
function [31:0] log2;
input [31:0] value;
[...]
Post by gabor
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
Cary R.
2010-09-28 22:40:06 UTC
Permalink
Post by Jonathan Bromley
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
Mark Curry
2010-09-29 00:59:53 UTC
Permalink
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
Post by Jonathan Bromley
[...]
Post by gabor
function [31:0] log2;
input [31:0] value;
[...]
Post by gabor
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
James Harris
2010-09-29 15:46:29 UTC
Permalink
Post by Jonathan Bromley
Post by James Harris
Warning, newbie question ahead.
Not newbie, but a very common need.
Post by James Harris
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,
  `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

Loading...