Search code examples
vhdlheader-filesquartus

VHDL standard layout & syntax for "header" file


IDE: Quartus 15

I'm new to VHDL programming so there are some nuances I am not used to (translating from C++). Whilst I have found resources for programming the "source" files, I've struggled to find anything for the "header" files.

In short, what is the standard layout / syntax of a VHDL "header" file?

To keep things simple, the use cases I'm interested in are declaring subtype and function references for use between "source" files.

I found the following code snippet here which has helped a little, but I'm still not sure of what the differences between package and package body are. I'm also unsure of where "work" comes from.

"Header":

    package DEFS is
        CONSTANT MAJOR_VERSION: INTEGER := 0;
        CONSTANT MINOR_VERSION: INTEGER := 22;
        CONSTANT MAXREG: integer := 52;
        TYPE REGS_TYPE is array (0 to MAXREG) of STD_LOGIC_VECTOR(15 downto 0);
        FUNCTION opndrn(inp: std_logic) return std_logic;
    end package DEFS;

    package body DEFS is
        FUNCTION opndrn(inp: std_logic) return std_logic IS
        begin
            CASE INP is
                WHEN '0' => return '0';
                WHEN OTHERS => return 'Z';
            END CASE;    
        end;
    end package body DEFS;

"Source":

    LIBRARY work;
    USE work.defs.all;

Any and all help is appreciated.


Solution

  • Thankfully, there are no header files in VHDL.

    What you have there is a package, which isn't ever "include"d in anything.

    Unlike a header file, a package is seperately compiled - into a library, which you can then "use" as you are doing.

    Unlike a header file, a package creates its own namespace. USE work.defs.all; imports that entire namespace, like using namespace defs in C++. Sometimes it's better practice to write USE work.defs; which then allows you to selectively use that namespace, and refer to defs.Major_Version in your source, thus (a) keeping your global namespace uncluttered, and (b) documenting where Major_Version is defined.

    Unlike a header file, a package encourages proper separation of interface and implementation. Your source can ONLY access things exported by the package, i.e. declared in the package not the package body. This allows information hiding, opaque types and generally better abstractions.

    For example you can declare Major_Version in the package but hide its actual value in the body (see "deferred constants")

    It's legal for both package and package body (interface and implementation) to be in the same file, but if you want to enforce separation of interface and implementation, the package body would be a separate file.

    Then you can modify the package body (implementation) and recompile it : nothing that uses the package needs recompilation (unless you also changed the interface.

    Any customer of the package only has to look at the package file, not the package body.


    In short, what you are doing looks pretty much OK.

    But you can go further in information hiding.

    And one thing to beware of is creating one all-purpose God package : far better to separate bus constants, functions etc into a "bus" package, register types (and maybe an enumeration of all their names) in a "registers" package, instructions in an "instructions" package etc.