I'm new to VHDL and currently try to get more complex data types working, so that code becomes more readable... However, it seems that I cannot define a type in VHDL before the entity
statement, which is using the type inside it's port statement.
Hence, it seems I need to somehow write my own library for that. I assume this must work, as types as std_logic_vector must be defined somehow too. Or did they hardcode these types into the VDHL compiler... and if so, why do we still need to include the libraries for it?
I want to do something like in the following code example. However, the compiler doesn't let my define the type in Test.vhdl as mentioned above. Anyhow, it seems to be valid to define the component in TOP.vhdl. I also would like to avoid to redefine the component and the data type in TOP.vhdl anyway. Is there a way to do this?
Test.vhdl
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
type MyRecordType is record -- Compiler complains about this!
x : std_logic_vector(2 downto 0); -- expects 'entity' or 'architecture'
y : std_logic_vector(2 downto 0);
end record;
entity Test is
port(
clk : in std_logic;
input : in MyRecordType;
output : out std_logic
);
end entity;
architecture RTL of Test is
begin
process (clk) is
begin
if rising_edge(clk) then
if (input.x = input.y) then
output <= '1';
else
output <= '0';
end if;
end if;
end process;
end;
TOP.vhdl
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
entity TOP is
end entity;
architecture SIM of TOP is
type MyRecordType is record
x : std_logic_vector(2 downto 0);
y : std_logic_vector(2 downto 0);
end record;
component Test is
port(
clk : in std_logic;
input : in MyRecordType;
output : out std_logic
);
end component;
constant period : time := 20 ns;
signal clk : std_logic;
signal result : std_logic;
signal myRecord : MyRecordType;
begin
dut : Test port map(clk => clk, input => myRecord, output => result);
clk <= not clk after period / 2;
process is
begin
myRecord.x <= "000";
myRecord.y <= "000";
wait for 20 ns;
if result = 0 then
report "TEST 1 - FAILED.";
else
report "TEST 1 - PASSED.";
end if;
myRecord.x <= "000";
myRecord.y <= "010";
wait for 20 ns;
if result = 1 then
report "TEST 2 - FAILED.";
else
report "TEST 2 - PASSED.";
end if;
wait;
end process;
end architecture;
1 Answer 1
To use a user defined type in a port declaration, you need to specify the type in a package and use that package in the context before the entity.
Utilities.vhdl
library ieee;
use ieee.std_logic_1164.all;
package utilities is
type MyRecordType is record
x : std_logic_vector(2 downto 0);
y : std_logic_vector(2 downto 0);
end record;
end package;
Test.vhdl
library ieee;
use ieee.std_logic_1164.all;
library myLib;
use myLib.Utilities.all;
entity Test is
port (
clk : in std_logic;
input : in MyRecordType;
output : out std_logic
);
end entity;
Top.vhdl
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library myLib;
use myLib.Utilities.all;
entity TOP is
end entity;
architecture SIM of TOP is
constant period : time := 20 ns;
signal clk : std_logic;
signal result : std_logic;
signal myRecord : MyRecordType;
begin
dut : entity myLib.Test
port map(
clk => clk,
input => myRecord,
output => result
);
end architecture;
Further hints:
- Don't use package
std_logic_unsigned
, usenumeric_std
instead. - Declaring the some type twice create two independent and incompatible types. VHDL is here more strict than C, because type equality is not based on structure, it's based on names. When you define a type twice, the fully qualified name of both types is different, thus you have incompatible types.
- You can omit component declarations and spare lot's of doubled code, when using direct entity instantiations.
- If you want to initialize
myRecord
at the beginning of the simulation, then initialize it when you declare the signal. UsingmyRecord.x <= "000";
has a delay of one delta cycle! - This expression will not work:
result = 0
, becauseresult
is no integer, you need to compare with'0'
. - You shouldn't
wait for 20 ns;
. Usewait for period;
.
-
\$\begingroup\$ Thanks for the solution and your very helpful additional advices! I've got two additional questions when reading your answer: a.) How is Utilities.vhdl actually declared to belong to "library myLib" instead of "work"? b.) Is the initialization at the declation via
:= VALUE
also synthesized or is it just working within the simulator (like thewait for...
statement)? \$\endgroup\$SDwarfs– SDwarfs2019年01月20日 22:29:16 +00:00Commented Jan 20, 2019 at 22:29 -
1\$\begingroup\$ libraries are handled by your tool, there is no additional file or instruction to make them part of a library. Your tool will offer you a mechanism to set a library name for each source file. If you use your tool on command line this will propably be
--work=myLib
. You shouldn't use a library namedwork
, becausework
is a special name in VHDL.work
refers to the current library, like athis
pointer in C#/Java, ... \$\endgroup\$Paebbels– Paebbels2019年01月20日 22:33:38 +00:00Commented Jan 20, 2019 at 22:33 -
1\$\begingroup\$ If the signal will be translated to a memory element and you have an FPGA as target platform, then initial values are synthesizable, otherwise they are ignored. Don't initialize signals, that will translate to wires, this might cover bugs, which you're trying to find in simulation with
X
andU
propagation. In your case it's a testbench, so you actually need to initialize it as I have shown, otherwise you have a delty-cycle delay, which might cause big problems (and hard to find problems....) \$\endgroup\$Paebbels– Paebbels2019年01月20日 22:36:01 +00:00Commented Jan 20, 2019 at 22:36 -
\$\begingroup\$ Yeah, that makes sense... thank's for also pointing out the potential pitfalls. \$\endgroup\$SDwarfs– SDwarfs2019年01月20日 22:38:22 +00:00Commented Jan 20, 2019 at 22:38