Counting number of asserted register bits in VHDL

B

bucketonuts

Hello,

I have a status register of width, R_SIZE. This is a generic so the register width may be different depending on the application. The R_SIZE is limited to values, 4, 8 and 16.

Each bit in the register is set by different module as an indication of that module's done status. These status bits are asserted for only one clock and may be asserted again as each module may run its application multiple times on different data.

I need to count the number of bits set in the status register on each clock and accumulate a total to match a predetermined "max" value.

I thought I'd use the VHDL 'generate' statement to compile RTL based on R_SIZE as follows.

=================== start RTL =====================
architecture behave of b is

component modx port(
mod_cmplt : out std_logic
);
end component modx;

signal mod_cmplt : std_logic_vector(15 downto 0);
signal cmplt_cnt : integer range 0 to 512;
signal next_cmplt_cnt : integer range 0 to 512;

begin
mod_cmplt(15 downto R_SIZE) <= (others => '0');

g1: for i in 0 to R_SIZE-1 generate
u_modx : modx port map(
mod_cmplt => mod_cmplt(i)
);
end generate;

process (mod_cmplt, cmplt_cnt)
begin
next_cmplt_cnt <= cmplt_cnt;

gen4: if R_SIZE = 4 generate
next_cmplt_cnt <= mod_cmplt(0) + mod_cmplt(1) + mod_cmplt(2) +
mod_cmplt(3) + cmplt_cnt;
end generate;

gen8: if R_SIZE = 8 generate
next_cmplt_cnt <= mod_cmplt(0) + mod_cmplt(1) + mod_cmplt(2) +
mod_cmplt(3) + mod_cmplt(4) + mod_cmplt(5) +
mod_cmplt(6) + mod_cmplt(7) + cmplt_cnt;
end generate;

gen16:if R_SIZE = 16 generate
next_cmplt_cnt <= mod_cmplt(0) + mod_cmplt(1) + mod_cmplt(2) +
mod_cmplt(3) + mod_cmplt(4) + mod_cmplt(5) +
mod_cmplt(6) + mod_cmplt(7) + mod_cmplt(8) +
mod_cmplt(9) + mod_cmplt(10) + mod_cmplt(11) +
mod_cmplt(12) + mod_cmplt(13) + mod_cmplt(14) +
mod_cmplt(15) + cmplt_cnt;
end generate

end process;

process (reset, clk)
begin
if (reset = '1') then
cmplt_cnt <= 0;
elsif (clk'event and clk='1')
cmplt_cnt <= next_cmplt_cnt;
end if;
end process

end behave;
================================ end RTL =================

The first error I get is a syntax error:

Error-[IEEEVHDLSYNTAXERR] Syntax error

gen4: if R_SIZE = 4 generate
^
Syntax error detected during VHDL parsing.

I don't know what to do about this. Is there a better way to code what I want the system to do?

Thank you.
 
G

GaborSzakacs

Hello,

I have a status register of width, R_SIZE. This is a generic so the register width may be different depending on the application. The R_SIZE is limited to values, 4, 8 and 16.

Each bit in the register is set by different module as an indication of that module's done status. These status bits are asserted for only one clock and may be asserted again as each module may run its application multiple times on different data.

I need to count the number of bits set in the status register on each clock and accumulate a total to match a predetermined "max" value.

I thought I'd use the VHDL 'generate' statement to compile RTL based on R_SIZE as follows.

=================== start RTL =====================
architecture behave of b is

component modx port(
mod_cmplt : out std_logic
);
end component modx;

signal mod_cmplt : std_logic_vector(15 downto 0);
signal cmplt_cnt : integer range 0 to 512;
signal next_cmplt_cnt : integer range 0 to 512;

begin
mod_cmplt(15 downto R_SIZE) <= (others => '0');

g1: for i in 0 to R_SIZE-1 generate
u_modx : modx port map(
mod_cmplt => mod_cmplt(i)
);
end generate;

process (mod_cmplt, cmplt_cnt)
begin
next_cmplt_cnt <= cmplt_cnt;

gen4: if R_SIZE = 4 generate
next_cmplt_cnt <= mod_cmplt(0) + mod_cmplt(1) + mod_cmplt(2) +
mod_cmplt(3) + cmplt_cnt;
end generate;

gen8: if R_SIZE = 8 generate
next_cmplt_cnt <= mod_cmplt(0) + mod_cmplt(1) + mod_cmplt(2) +
mod_cmplt(3) + mod_cmplt(4) + mod_cmplt(5) +
mod_cmplt(6) + mod_cmplt(7) + cmplt_cnt;
end generate;

gen16:if R_SIZE = 16 generate
next_cmplt_cnt <= mod_cmplt(0) + mod_cmplt(1) + mod_cmplt(2) +
mod_cmplt(3) + mod_cmplt(4) + mod_cmplt(5) +
mod_cmplt(6) + mod_cmplt(7) + mod_cmplt(8) +
mod_cmplt(9) + mod_cmplt(10) + mod_cmplt(11) +
mod_cmplt(12) + mod_cmplt(13) + mod_cmplt(14) +
mod_cmplt(15) + cmplt_cnt;
end generate

end process;

process (reset, clk)
begin
if (reset = '1') then
cmplt_cnt <= 0;
elsif (clk'event and clk='1')
cmplt_cnt <= next_cmplt_cnt;
end if;
end process

end behave;
================================ end RTL =================

The first error I get is a syntax error:

Error-[IEEEVHDLSYNTAXERR] Syntax error

gen4: if R_SIZE = 4 generate
^
Syntax error detected during VHDL parsing.

I don't know what to do about this. Is there a better way to code what I want the system to do?

Thank you.

I'm not up enough on VHDL to tell you if it's even valid
to have generate statements within a process, but the point is
that you don't need them here. Since mod_cmplt is always the
same size regardless of the value of R_SIZE, you can just use
if ... elsif ... else without the generates. Or even use a case
statement. Basically there's nothing in your equations that
requires generate statements. And note that because R_SIZE is
known at compile time it won't create extra logic.
 
K

kevin.neilson

Yeah, it seems like something much simpler like this would work fine:

process (reset, clk)
variable sum : integer := 0;
begin
if (reset = '1') then
cmplt_cnt <= 0;
elsif (clk'event and clk='1')
for k in 0 to R_SIZE - 1 loop
sum := sum + mod_cmplt(k);
end loop;
cmplt_cnt <= cmplt_cnt + sum;
end if;
end process;
 
B

bucketonuts

I'm not up enough on VHDL to tell you if it's even valid

to have generate statements within a process, but the point is

that you don't need them here. Since mod_cmplt is always the

same size regardless of the value of R_SIZE, you can just use

if ... elsif ... else without the generates. Or even use a case

statement. Basically there's nothing in your equations that

requires generate statements. And note that because R_SIZE is

known at compile time it won't create extra logic.

Thank you. I thought about this but didn't know what would happen to the adder if only 8 modx modules were instantiated. Even though the if-else branch for 16 modx's wouldn't be reached, the adder would still have inputs for modx[8]to modx[15] which would not be driven.
Also, you are correct, generate statements may not be located in a process.
Thanks again.
 
B

bucketonuts

Yeah, it seems like something much simpler like this would work fine:



process (reset, clk)

variable sum : integer := 0;

begin

if (reset = '1') then

cmplt_cnt <= 0;

elsif (clk'event and clk='1')

for k in 0 to R_SIZE - 1 loop

sum := sum + mod_cmplt(k);

end loop;

cmplt_cnt <= cmplt_cnt + sum;

end if;

end process;

Thank you - this looks interesting. So sum gets evaluated at clk'event in time for cmplt_cnt to be updated with the new value of sum?
 
G

GaborSzakacs

I'm not up enough on VHDL to tell you if it's even valid

to have generate statements within a process, but the point is

that you don't need them here. Since mod_cmplt is always the

same size regardless of the value of R_SIZE, you can just use

if ... elsif ... else without the generates. Or even use a case

statement. Basically there's nothing in your equations that

requires generate statements. And note that because R_SIZE is

known at compile time it won't create extra logic.

Thank you. I thought about this but didn't know what would happen to the adder
if only 8 modx modules were instantiated. Even though the if-else branch for
16 modx's wouldn't be reached, the adder would still have inputs for modx[8]
to modx[15] which would not be driven.
Also, you are correct, generate statements may not be located in a process.
Thanks again.

Actually the adder would not have extra undriven inputs _because_ those
branches are not reached, and the synthesizer only implements code that
is reached. For the cases where you don't have the maximum R_SIZE,
there would be some unused (and undriven) signals, but these would
generally get trimmed at or after physical synthesis. If you used
the other example with a loop, you could size the mod_cmplt vector
using R_SIZE and then there would be no unused signals. If you like
to avoid warnings during synthesis and build, then that is a cleaner
approach.
 
A

Andy

A nice little problem to illustrate VHDL RTL...

I don't know if it makes a real difference, but constraining the range of sum is seldom a bad idea:

variable sum : natural range 0 to mod_cmplt'length;

Don't forget to initialize sum to 0 before the loop, on every clock cycle. Variable declaration initializations in processes only happen once, at time0.

Also, syntactic tricks are required to add a std_logic bit to an integer and get an integer result:

sum := sum + to_integer(unsigned(0 => mod_cmplt(k)));

Or simply:

if mod_complt(k) = '1' then
sum := sum + 1;
end if;

Since cmplt_cnt is an integer, you need to make sure it never overflows. Presumably it would be set to zero once it reaches some limit where somethinghappens. If you want cmplt_cnt to roll over, either use mod (modulo operator) or make cmplt_cnt an unsigned instead of integer type (sum can still beinteger, and it cannot overflow).

More syntactic sugar: when iterating in a loop over the range of a vector, use vector'range as the loop index range:

for k in mod_cmplt'range loop

Finally, if you want to pipeline the sum and cmplt_cnt updates, just updatecnt_cmplt before sum is initialized:

pipelined: process (rst, clk) is
variable sum : natural range 0 to mod_cmplt'length;
begin
if rst = '1' then
cmplt_cnt <= 0;
sum := 0; -- used as register, so reset it
elsif rising_edge(clk) then
cmplt_cnt <= cmplt_cnt + sum; -- sum is register here
sum := 0; -- sum is combinatorial hereafter
for k in mod_complt'range loop
sum := sum + to_integer(unsigned(0 => mod_cmplt(k)));
end loop;
end if;
end process pipelined;

Andy
 
B

bucketonuts

A nice little problem to illustrate VHDL RTL...



I don't know if it makes a real difference, but constraining the range ofsum is seldom a bad idea:



variable sum : natural range 0 to mod_cmplt'length;



Don't forget to initialize sum to 0 before the loop, on every clock cycle.. Variable declaration initializations in processes only happen once, at time 0.



Also, syntactic tricks are required to add a std_logic bit to an integer and get an integer result:



sum := sum + to_integer(unsigned(0 => mod_cmplt(k)));



Or simply:



if mod_complt(k) = '1' then

sum := sum + 1;

end if;



Since cmplt_cnt is an integer, you need to make sure it never overflows. Presumably it would be set to zero once it reaches some limit where something happens. If you want cmplt_cnt to roll over, either use mod (modulo operator) or make cmplt_cnt an unsigned instead of integer type (sum can still be integer, and it cannot overflow).



More syntactic sugar: when iterating in a loop over the range of a vector, use vector'range as the loop index range:



for k in mod_cmplt'range loop



Finally, if you want to pipeline the sum and cmplt_cnt updates, just update cnt_cmplt before sum is initialized:



pipelined: process (rst, clk) is

variable sum : natural range 0 to mod_cmplt'length;

begin

if rst = '1' then

cmplt_cnt <= 0;

sum := 0; -- used as register, so reset it

elsif rising_edge(clk) then

cmplt_cnt <= cmplt_cnt + sum; -- sum is register here

sum := 0; -- sum is combinatorial hereafter

for k in mod_complt'range loop

sum := sum + to_integer(unsigned(0 => mod_cmplt(k)));

end loop;

end if;

end process pipelined;



Andy

Thanks, Andy. I'm new to VHDL. It took me most of the morning to figure outhow to add a std_logic bit to an integer (to_integer). You make some very good points in the rest of your post as well.
 
K

kevin.neilson

Andy,
Thanks--I wasn't sure if the variable had to be initialized to zero. I don't use variables often.

I elided the type conversion in my code snippet (even though that's where Ispend half my VHDL development time). I tried your std_logic->integer conversion above and Synplify didn't seem to be liking it, and I ended up having to do a ridiculous conversion like this:

sum := sum + to_integer(unsigned(std_logic_vector'(0=>mod_cmplt(k))));

This is why Verilog is awesome. You want to add a real number and the msb of an integer and a slice of a character string? No problem; no conversions required.
-Kevin
 
N

Nicolas Matringe

Le 16/05/2013 21:42, (e-mail address removed) a écrit :
This is why Verilog is awesome. You want to add a real number and the msb of an integer and
a slice of a character string? No problem; no conversions required.

That's weak typing's advantage. But it lets you so easily shoot yourself
in the foot...

Nicolas
 
A

Andy

Kevin,

Did you try to_integer(unsigned'(0 => mod_cmplt(k)))?

I usually use the if statement anyway. Much more readable.

Verilog: "Hold my beer and watch this!"

Andy
 
K

kevin.neilson

Andy: Yes, that does work, with the unsigned cast (using ') instead of the unsigned function. I don't really know the difference, but the cast does work.
-Kevin
 
A

Andy

Kevin, leaving out the ' was an unfortunate typo in my original suggestion.

Unsigned() is a built-in type conversion function from any closely related type to unsigned (a cast). In order for it to work, the argument must be statically determinable to be of a single type that is acceptable for the converion function.

The problem is (0 => [std_logic expression]) could be any of: slv, sulv, signed or unsigned, and perhaps others if additional packages are used. More importantly, all of those potential types are closely related to unsigned and allowable arguments for unsigned(). Thus the compiler cannot make a unique determination of which ONE of those types to use, so it throws an error (even though we know it really would not make a difference in the end).

Unsigned'() is a type designator. The type designator tells the compiler that the following anonymous expression IS of the type indicated. It is used when the following anonymous expression could be numerous types that would all "work", but VHDL needs to know which ONE of those that "will work" it should use.

Hope this helps,

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

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,528
Members
45,000
Latest member
MurrayKeync

Latest Threads

Top