"Interesting" behavior with aggregates

T

Tim Hubberstey

Hi all,

I was trying to create a mask for a vector in a semi-portable way when I
encountered an interesting behavior of aggregates. The problem is that
for 'outa', the aggregate ends up bring (7 downto 0) while for 'outb',
it ends up bring (0 to 7) with the result that the mask value is
incorrect. I solved this by doing it a different way so there is no
urgency.

------------------
-- in a separate package:
constant MaskBit : natural := 3;
------------------
signal Vector2Mask : std_logic_vector(7 downto 0) := (others => '1');
signal outa, outb : std_logic_vector(7 downto 0);

begin

-- outa = F7, aggregate is a "downto"
outa <= ( 7 downto MaskBit+1 => '1',
MaskBit => '0',
MaskBit-1 downto 0 => '1' );

-- outb = EF, aggregate is a "to"
outb <= Vector2Mask and
( 7 downto MaskBit+1 => '1',
MaskBit => '0',
MaskBit-1 downto 0 => '1' );
------------------

I looked at the LRM and I think that the issue is that there is no
reference for the aggregate in case 'outb' so it uses a 'default'
behavior. I didn't find the LRM to be very clear though, so I have a few
questions:

1. What exactly is happening here?

2. Is there a way to constrain the direction and/or size of an aggregate
so that I can force the direction to be the same ("downto" in this
example) in all cases? I know I can create a variable and assign it with
the aggregate but this seems unnecessarily complex.

Any insight is appreciated.

Thanks,
Tim

PS: I know this won't work if MaskBit is 0 or 7. What I *really* wanted
to do was:

outb <= Vector2Mask and (MaskBit => '0', others => '1');

but this produces an "'Others' is in unconstrained array aggregate"
error.
 
E

Egbert Molenkamp

Tim Hubberstey said:
Hi all,

I was trying to create a mask for a vector in a semi-portable way when I
encountered an interesting behavior of aggregates. The problem is that
for 'outa', the aggregate ends up bring (7 downto 0) while for 'outb',
it ends up bring (0 to 7) with the result that the mask value is
incorrect. I solved this by doing it a different way so there is no
urgency.

------------------
-- in a separate package:
constant MaskBit : natural := 3;
------------------
signal Vector2Mask : std_logic_vector(7 downto 0) := (others => '1');
signal outa, outb : std_logic_vector(7 downto 0);

begin

-- outa = F7, aggregate is a "downto"
outa <= ( 7 downto MaskBit+1 => '1',
MaskBit => '0',
MaskBit-1 downto 0 => '1' );

-- outb = EF, aggregate is a "to"
outb <= Vector2Mask and
( 7 downto MaskBit+1 => '1',
MaskBit => '0',
MaskBit-1 downto 0 => '1' );
------------------

I looked at the LRM and I think that the issue is that there is no
reference for the aggregate in case 'outb' so it uses a 'default'
behavior. I didn't find the LRM to be very clear though, so I have a few
questions:

1. What exactly is happening here?

Type std_logic_vector is an unconstrained array (NATURAL range ..)
Type natural is a subtype of integer, and
type integer is a range from low TO high.
The latter is the reason why, if there is no range given, the default range
is TO
(from the lowest, that is 0 in the subtype).
2. Is there a way to constrain the direction and/or size of an aggregate
so that I can force the direction to be the same ("downto" in this
example) in all cases? I know I can create a variable and assign it with
the aggregate but this seems unnecessarily complex.

I added 2 solution.
Solution 1 uses concatenation (in comment).
Solution 2 uses a tricky function 'feel downto'

I think that is what you want?

library ieee;
use ieee.std_logic_1164.all;
entity top is
port (outa, outb : out std_logic_vector(7 downto 0));
end top;

architecture test of top is


------------------
-- in a separate package:
constant MaskBit : natural := 3;
------------------
signal Vector2Mask : std_logic_vector(7 downto 0) := (others => '1');
-- signal outa, outb : std_logic_vector(7 downto 0);

function feels_downto( i : std_logic_vector) return std_logic_vector is
variable res : std_logic_vector(i'reverse_range);
begin
if i'ascending then
for j in i'range loop
res(j):=i(j);
end loop;
return res;
else
return i;
end if;
end feels_downto;
begin

-- outa = F7, aggregate is a "downto"
outa <= ( 7 downto MaskBit+1 => '1',
MaskBit => '0',
MaskBit-1 downto 0 => '1' );

--Solution 1
-- outb <= Vector2Mask and
-- ( (7 downto MaskBit+1 => '1')&
-- (MaskBit => '0')&
-- (MaskBit-1 downto 0 => '1' ));

--Solution 2
outb <= Vector2Mask and
feels_downto (( 7 downto MaskBit+1 => '1',
MaskBit => '0',
MaskBit-1 downto 0 => '1' ));

end test;

Egbert Molenkamp
 
M

Mike Treseler

Tim Hubberstey said:
PS: I know this won't work if MaskBit is 0 or 7. What I *really* wanted
to do was:

outb <= Vector2Mask and (MaskBit => '0', others => '1');

but this produces an "'Others' is in unconstrained array aggregate"
error.

OK. Solution 3:

-- ...
subtype vec is std_logic_vector(7 downto 0);
signal Vector2Mask : vec := (others => '1');
-- ...
begin
outd <= Vector2Mask and vec'(MaskBit => '0', others => '1');

-- Mike Treseler
 
T

Tim Hubberstey

Mike said:
OK. Solution 3:

-- ...
subtype vec is std_logic_vector(7 downto 0);
signal Vector2Mask : vec := (others => '1');
-- ...
begin
outd <= Vector2Mask and vec'(MaskBit => '0', others => '1');

Thanks, Mike. That will certainly work but it isn't really an
improvement over:

variable mask : std_logic_vector(7 downto 0)
:= (MaskBit => '0', others => '1');
....
outd <= Vector2Mask and mask;

I was hoping there was some way that didn't require a long-winded set of
declarations. An obvious (but unfortunately incorrect) solution would be
something like:

outd <= Vector2Mask and std_logic_vector(7 downto 0)'(MaskBit => '0',
others => '1');
 
T

Tim Hubberstey

Egbert said:
Type std_logic_vector is an unconstrained array (NATURAL range ..)
Type natural is a subtype of integer, and
type integer is a range from low TO high.
The latter is the reason why, if there is no range given, the default range
is TO
(from the lowest, that is 0 in the subtype).

Thanks for the explanation, Egbert. It all makes sense now.
[ useful function snipped ]

The bottom line seems to be that it is at least as much (error-prone)
work to constrain an aggregate as it is to use a temporary variable so I
think I'll stick to the variable solution.
 
M

Mike Treseler

Tim Hubberstey said:
Thanks, Mike. That will certainly work but it isn't really an
improvement over:

variable mask : std_logic_vector(7 downto 0)
:= (MaskBit => '0', others => '1');
...
outd <= Vector2Mask and mask;

I agree.
Hard to guess what someone will like.
Any reason mask can't be a constant?
I was hoping there was some way that didn't require a long-winded set of
declarations.

The declaration
subtype vec is std_logic_vector(7 downto 0);
seems short-winded to me.
And it could be used more than once
in the process or architecture.

-- Mike Treseler
 
T

Tim Hubberstey

Mike said:
Any reason mask can't be a constant?

Not really. It was all the result of iterative code writing. I started
out with

outd <= Vector2Mask and (MaskBit => '0', others => '1');

but then that gave errors so I modified it until it stopped complaining.
I then discovered the "interesting" feature of the unexpected range
direction and got distracted by trying to figure out what the !@#$$%^
was going on.
 

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,733
Messages
2,569,439
Members
44,829
Latest member
PIXThurman

Latest Threads

Top