1
\$\begingroup\$

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;
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

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, use numeric_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. Using myRecord.x <= "000"; has a delay of one delta cycle!
  • This expression will not work: result = 0, because result is no integer, you need to compare with '0'.
  • You shouldn't wait for 20 ns;. Use wait for period;.
\$\endgroup\$
4
  • \$\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 the wait for... statement)? \$\endgroup\$
    – SDwarfs
    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 named work, because work is a special name in VHDL. work refers to the current library, like a this pointer in C#/Java, ... \$\endgroup\$
    – Paebbels
    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 and U 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
    Jan 20, 2019 at 22:36
  • \$\begingroup\$ Yeah, that makes sense... thank's for also pointing out the potential pitfalls. \$\endgroup\$
    – SDwarfs
    Jan 20, 2019 at 22:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.