Electronic – use custom data types to exchange data between modules in VHDL

hdlvhdl

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;

Best Answer

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