Alias array / array of aliases

C

Colin Beighley

Hello,

I'm looking to do something like the following.

--code-

signal signal_a, signal b : unsigned(3 downto 0);

type registers is array 0 to 1 of unsigned(3 downto 0);

alias my_regs : registers is (signal_a,signal_b);

--end code--

So essentially I have a bunch of signals in a module and want to group
them as an array of registers using aliases. The other way around is
possible - declaring the array of registers as signals and then
declaring the individual signals using aliases. However, I can't do it
as I've shown, at least not using Xilinx's tools. Any suggestions?

Thanks,
Colin
 
P

Pieter Hulshoff

Colin said:
Hello,

I'm looking to do something like the following.

--code-

signal signal_a, signal b : unsigned(3 downto 0);

type registers is array 0 to 1 of unsigned(3 downto 0);

alias my_regs : registers is (signal_a,signal_b);

--end code--

So essentially I have a bunch of signals in a module and want to group
them as an array of registers using aliases. The other way around is
possible - declaring the array of registers as signals and then
declaring the individual signals using aliases. However, I can't do it
as I've shown, at least not using Xilinx's tools. Any suggestions?

I'm not sure why you want to use an alias for this. Why not use something like:

declaration:
SIGNAL my_regs : registers;

concurrent assignment:
my_regs <= (signal_a, signal_b);

? (exact syntax not checked, but I'm sure you get the idea :) )

Kind regards,

Pieter Hulshoff
 
G

Gabor

Pieter said:
I'm not sure why you want to use an alias for this. Why not use something like:

declaration:
SIGNAL my_regs : registers;

concurrent assignment:
my_regs <= (signal_a, signal_b);

? (exact syntax not checked, but I'm sure you get the idea :) )

Kind regards,

Pieter Hulshoff

I've run into similar situations. The problem is that concurrent
assignments are one-way. What if I'm using these signals to
connect inout ports of sub-modules? Usually this gets painful
when you need to use a generate loop.

-- Gabor
 
M

Mike Treseler

I don't use aliases because they are invisible in simulation.
If you want an array of registers, you will need to declare
a type and subtype as shown below. This has nothing to
do with Xilinx tools. It is the vhdl language.

-- Mike Treseler

package stack_pkg is
constant reg_len_c : positive := 32;
constant array_len_c : positive := 32;
subtype reg_t is std_logic_vector(reg_len_c-1 downto 0);
type regs_t is array (0 to array_len_c-1) of reg_t;
constant reg_init_c : reg_t := (others => '0');
end package stack_pkg;
 
C

Colin Beighley

It's not quite as elegant as I had hoped, but here's what I've been
doing.

Leveraging the resolved nature of the std_logic (and hence slv,
signed, and unsigned), I'm putting a procedure at the end of my
sequential assignment process for the state machine of the module that
does the address based IO. In each clock cycle the companion
combinatorial process decides the values that will be assigned to the
registers in the procedure update_regs, and then if the external logic
wants to write to a register the value written to that register will
resolve to the external logic's value.

I tend to use the resolution capabilities of std_logic and derived
types pretty heavily in this way, does anyone have any comment on best
practices in this regard?

Note - it's easy to use signals in the module that are of greater /
smaller length than the data bus, you just need to be careful with
your conversions to signed/unsigned know what you're doing with the
unused bits. If they're greater you have to use more than 1 address,
for instance unused bits + MSb's at addr X and LSb's at addr X + 1.

--CODE--
entity module is
port(
CLK,RST : in std_logic;

REG_ADDR : in unsigned(1 downto 0);
REG_WR : in std_logic;
REG_DATA_IN : in std_logic_vector(3 downto 0);
REG_DATA_OUT : out std_logic_vector(3 downto 0)
);
end entity;

architecture behavioral of module is
signal sig_a_signed : signed(3 downto 0);
signal sig_b_slv : std_logic_vector(3 downto 0);
signal sig_c_unsigned : unsigned(3 downto 0);
begin

sequential_assignments : process(RST,CLK)
procedure reset_regs;
procedure update_regs;

procedure register_io is
begin
case to_integer(REG_ADDR) is
when 0 =>
if REG_WR = '1' then sig_a_signed <= signed(REG_DATA_IN); end
if;
REG_DATA_OUT <= std_logic_vector(sig_a_signed);
when 1 =>
if REG_WR = '1' then sig_b_slv <= REG_DATA_IN; end if;
REG_DATA_OUT <= sig_b_slv;
when 2 =>
if REG_WR = '1' then sig_c_unsigned <= unsigned(REG_DATA_IN);
end if;
REG_DATA_OUT <= std_logic_vector(sig_c_unsigned);
when others =>
REG_DATA_OUT <= (others => '-');
end case;
end procedure;
begin
if RST = '1' then
reset_regs;
elsif rising_edge(CLK) then
update_regs;
register_io;
end if;
end process sequential_assignments;

end architecture;
--END CODE--
 
C

Colin Beighley

Note that in the last post I didn't include the combinatorial process
or flesh out the reset or update procedures, as I thought it was
unnecessary to explain.
 
K

KJ

It's not quite as elegant as I had hoped, but here's what I've been
doing.

Leveraging the resolved nature of the std_logic (and hence slv,
signed, and unsigned), I'm putting a procedure at the end of my
sequential assignment process for the state machine of the module that
does the address based IO.

Your example does not demonstrate any use of 'the resolved nature of
the std_logic', what you have will work just as well with the
unresolved type std_ulogic. That's OK, I wouldn't recommend using
'the resolved nature of the std_logic' in any code intended to be
synthesized...certainly not for addressable registers.
In each clock cycle the companion
combinatorial process decides the values that will be assigned to the
registers in the procedure update_regs, and then if the external logic
wants to write to a register the value written to that register will
resolve to the external logic's value.

What you've described here (about 'resolve to the external logic's
value') is not what you've shown in your code. Your example code
simply shows addressable registers...and you should ask yourself why
you think you need a separate combinatorial process, the clocked
process you have is all you need...much cleaner.
I tend to use the resolution capabilities of std_logic and derived
types pretty heavily in this way, does anyone have any comment on best
practices in this regard?

1. You're not using the resolution capabilities of std_logic...I'm not
sure why you think you are. I'm guessing that since you have an
'update_regs' (not shown) and it probably updates the same signals as
'register_io' that you think this is using resolved logic. If that's
the case then you're mistaken, the 'register_io' procedure is simply
higher priority than 'update_regs' since it will have the final word.
This is not intended as a criticism of your code, just that your
terminology about using resolved logic is not correct. Resolved logic
has to do with multiple drivers of a signal...and a single process
(even with multiple procedures) can never create multiple drivers of
any signal.

2. If you were making use of the resolution capabilities of std_logic
in your example code and that code was intended to be synthesized,
that would be a mistake. Since you're not, your code looks OK.
Note - it's easy to use signals in the module that are of greater /
smaller length than the data bus, you just need to be careful with
your conversions to signed/unsigned know what you're doing with the
unused bits. If they're greater you have to use more than 1 address,
for instance unused bits + MSb's at addr X and LSb's at addr X + 1.

I tend to define a record that defines all of the bits that are
writable whether they are used to control anything or not like this...
type t_THIS_PORT is record
Reserved: std_ulogic_vector(31 downto 24);
Pointer: std_ulogic_vector(23 downto 0);
end record t_THIS_PORT;

Next I define a pair of functions called 'to_std_ulogic_vector' and
'from_std_ulogic_vector' that work with this record type and do
exactly what the name suggests.

Repeat this for all read/write registers.

Now the register updates (such as your 'sig_a_signed <=
signed(REG_DATA_IN);') become 'sig_a <=
from_std_ulogic_vector(REG_DATA_IN'). By itself, this isn't really
much different than what you have, but it also cleanly takes care of
things like bit widths not matching the external updater as well as
when you have ports with lot of bit fields and you want to change
those definitions around a bit. In my case, all I would change is the
code inside the 'to_std_ulogic_vector' and 'from_std_ulogic_vector'
functions and re-synthesize. In fact, if all that is changing is the
widths/bit locations of existing fields (i.e. not adding/removing any
fields), then all I update is the record definition without touching
the source code for the function at all. No having to hunt through
and places where bit 9 needs to be used rather than bit 8 to control
something.

Kevin Jennings
 
P

Paul Uiterlinden

Colin said:
--CODE--
entity module is
port(
CLK,RST : in std_logic;

REG_ADDR : in unsigned(1 downto 0);
REG_WR : in std_logic;
REG_DATA_IN : in std_logic_vector(3 downto 0);
REG_DATA_OUT : out std_logic_vector(3 downto 0)
);
end entity;

architecture behavioral of module is
signal sig_a_signed : signed(3 downto 0);
signal sig_b_slv : std_logic_vector(3 downto 0);
signal sig_c_unsigned : unsigned(3 downto 0);
begin

sequential_assignments : process(RST,CLK)
procedure reset_regs;
procedure update_regs;

procedure register_io is
begin
case to_integer(REG_ADDR) is
when 0 =>
if REG_WR = '1' then sig_a_signed <= signed(REG_DATA_IN); end
if;
REG_DATA_OUT <= std_logic_vector(sig_a_signed);
when 1 =>
if REG_WR = '1' then sig_b_slv <= REG_DATA_IN; end if;
REG_DATA_OUT <= sig_b_slv;
when 2 =>
if REG_WR = '1' then sig_c_unsigned <= unsigned(REG_DATA_IN);
end if;
REG_DATA_OUT <= std_logic_vector(sig_c_unsigned);
when others =>
REG_DATA_OUT <= (others => '-');
end case;
end procedure;
begin
if RST = '1' then
reset_regs;
elsif rising_edge(CLK) then
update_regs;
register_io;
end if;
end process sequential_assignments;

end architecture;
--END CODE--

This code does not make any sense to me. In procedure register_io you drive
a new value to say sig_a_signed. At the same moment, you drive the value of
sig_a_signed to REG_DATA_OUT. But that is the *old* value of sig_a_signed.
The new value appears on sig_a_signed only after a delta delay.

Have you simulated this code? Admittedly, I have not, other than reading the
code and perform some mental simulation.

As KJ already pointed out, there isn't any resolving involved in this code.
That was the thing that made me look at your code in the first place.
 
K

KJ

This code does not make any sense to me. In procedure register_io you drive
a new value to say sig_a_signed. At the same moment, you drive the value of
sig_a_signed to REG_DATA_OUT. But that is the *old* value of sig_a_signed..
The new value appears on sig_a_signed only after a delta delay.

I believe that the 'REG_*' interface signals represent a typical read/
write interface to some external processor. Presumably that processor
would need the ability to read from registers hence the 'REG_DATA_OUT
<= ...' statements. Putting this inside the clocked process presumes
that the processor is expected to wait one clock cycle after setting
the address before REG_DATA_OUT would be valid. Far better to have an
explicit read command and wait (or acknowledge) handshake signals for
an interface definition, but that's off topic.

Kevin Jennings
 
C

Colin Beighley

Thanks for the advice, guys! My understanding of resolved signals was
that you could do multiple assignments within a process to a signal
and that only the last one would take affect. Is this not an attribute
specific only to resolved signals? I have simulated this and it seems
to work fine, is there a reason I shouldn't synthesize a design like
this?
 
K

KJ

Thanks for the advice, guys! My understanding of resolved signals was
that you could do multiple assignments within a process to a signal
and that only the last one would take affect.

Remove the word 'resolved' from your sentence and your sentence will
be correct.
Is this not an attribute
specific only to resolved signals?

No, it is true for all signals.
I have simulated this and it seems
to work fine, is there a reason I shouldn't synthesize a design like
this?

I didn't see anything wrong in the method demonstrated by your example
code. Should synthesize just fine.
 
T

Tricky

Again, thanks for the advice, KJ. I guess I should read up on resolved
signals.

resolution functions need to be defined for a type. The only resolved
type in VHDL is std_logic. This allows std_logic to be driven from
multiple processes. The main use of this is to simulate tri-state
busses.

with a resolution function, you can do this in your code (outside of a
process)

a <= '1';
a <= '0';

and when you simulate it you get 'X'

if you tried this with a std_ulogic, it would give you an error when
you compiled the code (before you even got to the simulation) because
std_ulogic is not resolved, and multiple drivers are banned.
 

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,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top