-- 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;
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
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
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.
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.