Possible to generate individual cases within a case statement?

L

longbrmb

Hello,

I have a module that I'm trying to implement generically. The problem
stems from the fact that the signal used in the case statement has a
variable width - it gets set by two generic parameters. The
difficulty is that when the width changes, the number of possible
cases changes and all cases must be covered in order for the code to
compile. The code is posted below and essentially boils down to a
priority mux with a variable number of inputs. Is there anyway to
generate the appropriate number of cases based on the width of the
selector?

"bits" and "highbit" are std_logic_vectors

process(clk) is
variable hbit_tmp : natural;
variable bits_tmp : natural;
begin
if(rising_edge(clk)) then
bits_tmp := to_integer(unsigned(bits));
case bits_tmp is
when 1 => hbit_tmp := numOutBits;
when 2#10# to 2#11# => hbit_tmp := numOutBits + 1;
when 2#100# to 2#111# => hbit_tmp := numOutBits + 2;
when 2#1000# to 2#1111# => hbit_tmp := numOutbits + 3;
when 2#10000# to 2#11111# => hbit_tmp := numOutBits +
4;
when 2#100000# to 2#111111# => hbit_tmp := numOutBits +
5;
when others => hbit_tmp := numOutBits - 1;
end case;
highbit <= std_logic_vector(to_unsigned(hbit_tmp,
highbit'length));
end if;
end process;
 
J

Jonathan Bromley

I have a module that I'm trying to implement generically. The problem
stems from the fact that the signal used in the case statement has a
variable width - it gets set by two generic parameters. The
difficulty is that when the width changes, the number of possible
cases changes and all cases must be covered in order for the code to
compile. The code is posted below and essentially boils down to a
priority mux with a variable number of inputs. Is there anyway to
generate the appropriate number of cases based on the width of the
selector?

Yes, but not the way you're doing it (I don't think).
You certainly can't make the number of branches in a
case statement configurable.

For this rather regular problem (find highest-numbered 1 bit in
a word) there's no doubt that a function provides the most
elegant and convenient solution, because functions *can*
resize themselves in response to a generic or (better) an
array size.

Find the highest-numbered 1 bit in vector "bits" using
a function, thus:

-- represent highest-1s bit position with an integer;
-- value -1 means no 1s at all (bits=0); value 0 means
-- only the least significant bit is set; value 1 means
-- the next-more significant bit is set (000...00010 or 11).
-- Synthesis will work out how many bits are needed for this.
subtype bit_position is integer range -1 to bits'length-1;

-- Function to calculate highest-1s bit position
function top_1_bit(bits: std_logic_vector) return bit_position
is
variable b: std_logic_vector(bits'length-1 downto 0);
variable result: bit_position;
begin
b := bits;
result := -1;
for i in b'reverse_range loop
if b(i) = '1' then
result := i;
end if;
end loop;
return i;
end;

Now, this is almost certainly not the most efficient way to
do it; but it will work, and we can do something about
efficiency later, by rewriting the function.

Given such a function, I *think* your process simply
becomes:

process(clk)
begin
if rising_edge(clk) then
highbit <= std_logic_vector(to_unsigned(
top_1_bit(bits) + numOutBits, highbit'length));
end if;
end process;

Now we need to work on the synthesis efficiency of our
find-top-bit function. This has been discussed many
times before here; one attractive approach is to
use a recursive function doing binary-chop search
for the highest 1 bit. It doesn't handle the
special case of all-zero, but you can deal with
that in a different way.... see later.

-- ASSUMES that the vector has been padded to be
-- an exact power of 2 in length, and is at least
-- 2 bits in length. You can write assertions for
-- that, if you wish. Uses unsigned rather than
-- std_logic_vector, to facilitate zero comparison.
function top_1_bit(bits: unsigned)
return unsigned is
constant L: positive := bits'length;
constant H: natural := L/2;
constant B: unsigned(L-1 downto 0) := bits;
begin
assert L>1
report "Bad call to top_1_bit"
severify fatal;
if L=2 then
return B(1 downto 1);
elsif B(L-1 downto H) = 0 then
return '0' & top_1_bit(B(H-1 downto 0));
else
return '1' & top_1_bit(B(L-1 downto H));
end if;
end;

Finally you need to pad your original vector by
appending a '1' bit to its LSB end (that provides a
fix for the "all zeros" case) and padding its MSB
end with enough zeros to make the total length an
exact power of 2:

-- Constant function to compute the smallest
-- exact power of 2 that's larger than "len"
function next_power_2(len: positive) return positive is
variable n: positive;
begin
n := 1;
while n <= len loop
n := n * 2;
end loop;
return n;
end;

function pad(bits: unsigned) return unsigned is
constant N: positive := next_power_2(bits'length);
variable padded: unsigned(N-1 downto 0);
begin
padded := (others => '0');
padded(0) := '1';
padded(bits'length downto 1) := bits;
return padded;
end;

Now you can ask for top_1_bit(padded(something))
and if "something" is exactly 0 you'll get 0 as the
result; if the LSB only is set, you'll get 1; and
so on, up to the unsigned representation of
something'length if the topmost bit is set. Obviously
this has an offset of 1 relative to your problem, but
equally obviously that's rather easy to fix.

HTH
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
(e-mail address removed)
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
M

Mike Treseler

I have a module that I'm trying to implement generically. The problem
stems from the fact that the signal used in the case statement has a
variable width - it gets set by two generic parameters. The
difficulty is that when the width changes, the number of possible
cases changes and all cases must be covered in order for the code to
compile.

Sounds like a function would like to break out.
Something like this:
http://home.comcast.net/~mike_treseler/priority_mux.vhd
Try it and see. No guarantee.

-- Mike Treseler
 
M

Marcus Harnisch

I have a module that I'm trying to implement generically. The problem
stems from the fact that the signal used in the case statement has a
variable width - it gets set by two generic parameters. The
difficulty is that when the width changes, the number of possible
cases changes and all cases must be covered in order for the code to
compile. The code is posted below and essentially boils down to a
priority mux with a variable number of inputs. Is there anyway to
generate the appropriate number of cases based on the width of the
selector?

Isn't that a job for our old fellow "binary logarithm"?

,----
| process(clk) is
| variable hbit_tmp : natural;
| variable bits_tmp : natural;
| begin
| if(rising_edge(clk)) then
| bits_tmp := to_integer(unsigned(bits));
|
|
| if (bits /= (bits'range => '0')) then
| hbit_tmp := numOutBits + ld2(bits_tmp);
| else
| hbit_tmp := numOutBits - 1;
| end if;
|
| highbit_1 <= std_logic_vector(to_unsigned(hbit_tmp, highbit_1'length));
| end if;
| end process;
`----

The implementation of ld2() is left as an exercise.

Regards
Marcus
 
A

Andy

Very nice post...

I've seen synplify do some pretty nice optimizations of priority
loops, so I would try the simple loop first and see if it meets your
performance requirements. If it does, it is much simpler to write,
understand, and maintain. But if it does not, Jonathan's second
implementation is excellent.

Also, you can assign the initial value of the variable b to bits in
the declaration:

variable b: std_logic_vector(bits'length-1 downto 0) := bits;

This works for subprograms (functions & procedures) only, not for
processes. b gets set to bits whenever the function is entered,
whereas a process is only entered once, so an initial value in a
process is only set once.

Finally, for a loop that will at least simulate faster:

for i in b'range loop
if b(i) = '1' then
result := i;
exit;
end if;
end loop;

This avoids running the entire loop unless no bits are set.

Andy
 
J

Jonathan Bromley

you can assign the initial value of the variable b to bits in
the declaration:

variable b: std_logic_vector(bits'length-1 downto 0) := bits;

This works for subprograms (functions & procedures) only, not for
processes. b gets set to bits whenever the function is entered,
whereas a process is only entered once, so an initial value in a
process is only set once.

All perfectly true, but I've had trouble in the past with some
synth tools not accepting such variable initializations in
subprograms. Happily, I can't find any tools today that
don't accept it. But old habits die hard :)
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
(e-mail address removed)
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
L

longbrmb

Thanks for all the tips guys! I ended up using Andy's version of the
loop in Jonathan's first solution. The resulting hardware had the
exact same number of slices and everything, so it must have inferred
the same logic that I described in my case statement. We didn't
really cover functions in school, so I'm not used to thinking about
how I could accomplish things with them. Looks like I need to keep an
open mind!

Matt
 
M

Mike Treseler

Thanks for the examples, Jonathan.

-- Mike Treseler


_______________
Couple of typos:
....
end loop;
-- return i;
return result;

....
report "Bad call to top_1_bit"
-- severify fatal;
severity failure;
 
J

Jonathan Bromley

Couple of typos:
...
end loop;
-- return i;
return result;

whoops, indeed so
...
report "Bad call to top_1_bit"
-- severify fatal;
severity failure;

Well, I think I can probably be forgiven for
"severify", but "fatal" is a nasty case of
SystemVerilog leaking into VHDL and should
be exposed to public ridicule as vigorously
as possible :)
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
(e-mail address removed)
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
A

Andy

Well, I think I can probably be forgiven for
"severify", but "fatal" is a nasty case of
SystemVerilog leaking into VHDL and should
be exposed to public ridicule as vigorously
as possible :)
--


Actually,

constant fatal : severity_level := failure;

was in some package, the reference to which was left out for brevity.

yeah, that's it! ;^)

Andy
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top