This is mostly for Jonathan Drolet but there's also a lesson here.
Note that parameter argument to my_function supplies a type mark with a subtype indication. This is valid in VHDL.
library ieee;
use ieee.std_logic_1164.all;
entity myfunc is
end entity;
architecture foo of myfunc is
function my_function(lv: std_logic_vector(3 downto 0))
return std_logic_vector is
begin
return lv & "000";
end function;
constant fumble: std_logic_vector (3 downto 0) := x"E";
signal humble: std_logic_vector (6 downto 0);
begin
humble <= my_function(fumble);
end architecture;
This code analyzes, elaborates and simulates.
Note that the return mark is an unconstrained subtype and the result value is assigned to a declared signal with a subtype indication (supplying an index constraint).
There are rules specifically for determining the effective value of a signal that require the bounds be checked after an implicit subtype conversion. Providing a return value that doesn't match the bounds of humble
would result in a run time error causing simulation to end without successfully completing.
In other words a constraint isn't necessary here. The return value subtype indication is derived internally, and as is shown in this from the input parameter.
Although in your example unsigned
and signed
are both arrays of the same element type std_logic
, this is not the same as a subtype. A subtype is when one type is a limited subset of another type, for example:
subtype my_type is std_logic_vector(3 downto 0);
subtype eight_bit_int is integer range 0 to 255;
A feature of a subtype is that it can be automatically converted to and from the parent type, so I can do:
signal a : std_logic_vector(3 downto 0);
signal b : my_type;
signal c : integer;
signal d : eight_bit_int_type;
...
-- These should both work fine
b <= a;
c <= d;
If you want to connect differing types in a port map, you might have to use type casts or type conversions, or both. Using your example entity, you might write something like this:
signal a_actual : std_logic_vector(3 downto 0);
signal b_actual : std_logic_vector(3 downto 0);
signal c_actual : std_logic_vector(3 downto 0);
signal d_actual : std_logic;
...
-- Converting from std_logic_vector to signed or unsigned only requires a cast
dummy_inst : dummy
port map(
a => a_actual,
b => std_logic_vector(b_actual),
c => std_logic_vector(c_actual),
d => actual
);
If you wanted to connect an input, for example b
to an integer, you would have to use a type conversion and a cast, and the line would look like this:
b => std_logic_vector(to_signed(b_integer, 4)), -- 4 is the length of the port
Best Answer
In the first case you are simply declaring a type. You would have to then instantiate a signal of that type like so:
My preference is to append
_type
to type names, so your type would beWORD_type
, and your signal declaration could then be:Your second case is a plain
std_logic_vector
. Using your own type instead of a predefined one might make your code more readable and/or provide more compile time checks on signal assignments, etc. It also means that if the length of your 'word' changes, you might only have to change the original type definition, instead of having to go round everywhere changing15 downto 0
into31 downto 0
, or whatever the new size might be.You can allow automatic conversion between your type, and the base type, by making your type a
subtype
:This allows you to do the following:
...