the problem with packages and generics and user defined types(arrays, records, etc)

W

wallge

So here's my problem - and it is starting to drive me really crazy as
I start to become a seasoned VHDL designer.

I have lots of modules I have designed - and many of them have their
own associated packages to define some types that are used inside of
them, as well as constants that may be pertinent to each component's
functioning.

Consequently, anytime I want to instantiate components in the design I
am using, I wind up having to drag along a bunch of packages - that
occasionally have contradictory type definitions or constants that
wind up making up a bunch of work for me when I go to plug everything
together.

So I decided that I would like to use generics for each component
instead of packages.
The problem with this (as far as I can tell) is that you can't pass
types

(eg. array_type is array (3 downto 0) of unsigned(7 downto 0))

as generics.
You have to have a package in order to use any user defined types that
are shared (maybe used as ports) between multiple entities.

What I would like is a way of sharing custom type definitions between
multiple entities
-- where they type definitions would be based on generics passed to
the component
when it is instantiated, without having to having to keep track of a
bunch of packages.

Does anyone have a solution to this problem?

thanks in advance.
 
K

KJ

I have lots of modules I have designed - and many of them have their
own associated packages to define some types that are used inside of
them, as well as constants that may be pertinent to each component's
functioning.
OK.

Consequently, anytime I want to instantiate components in the design I
am using, I wind up having to drag along a bunch of packages - that
occasionally have contradictory type definitions or constants that
wind up making up a bunch of work for me when I go to plug everything
together.
Just what do you mean by 'contradictory type definitions or
constants'? Reusing the same type name or constant names in multiple
packages? If so, then you can use the 'path name' to the thing in
question.

Example:
Instead of...
use pkg_this_package.all;
use pkg_that_package.all;
...
signal abc : my_type;
signal xyz : my_type;

you can have (note: no need for 'use' statements)
signal abc : work.pkg_this_package.my_type;
signal xyz : work.pkg_that_package.my_type;

Constants as well can be referred to similarly

constant ZZZ: integer := work.pkg_this_package.some_const;

Again, I'm not sure if this is the type of issue you're running across
or not.
So I decided that I would like to use generics for each component
instead of packages.
The problem with this (as far as I can tell) is that you can't pass
types

(eg. array_type is array (3 downto 0) of unsigned(7 downto 0))
You're correct, you can not do this.
as generics.
You have to have a package in order to use any user defined types that
are shared (maybe used as ports) between multiple entities.
Sharing between entities is the only reason you would put it in the
package to begin with. Any other 'local' constants would be put in
the architecture or process body.
What I would like is a way of sharing custom type definitions between
multiple entities
-- where they type definitions would be based on generics passed to
the component
when it is instantiated, without having to having to keep track of a
bunch of packages.

Does anyone have a solution to this problem?
I don't think there is a solution along the lines of thinking that
you're proposing.

Kevin Jennings
 
M

Mike Treseler

wallge wrote:
....
Consequently, anytime I want to instantiate components in the design I
am using, I wind up having to drag along a bunch of packages - that
occasionally have contradictory type definitions or constants that
wind up making up a bunch of work for me when I go to plug everything
together.

I agree with KJ.
Type and constant declarations should be left
in process or architecture scope unless they
are related to the top ports, or custom functions.

If a declaration is packaged, it should be well-named.
Identifiers like count, state, i, n etc. should be locals.
So I decided that I would like to use generics for each component
instead of packages.

I would stick with packages, but tidy them up.
Sometimes I cut and paste exactly what I need
into one package for each significant synthesis entity.


-- Mike Treseler
 
W

wallge

wallge wrote:

...


I agree with KJ.
Type and constant declarations should be left
in process or architecture scope unless they
are related to the top ports, or custom functions.


Typically, the problem is that I have custom types
(records, arrays, etc) that are used as ports.
The definition of these types is done in a package.
This allows the various entities that use the
custom types as ports to connect to one another.
The thing is, I don't want to have to have to
keep track of ,say, 10 different packages
when putting a design together - typically
I have to edit several of the package definitions
in order to make everything agree, and if
I forget one here or there, the design won't
compile.

What I really want is a kind of functionality
similar to C++ template classes, where you can
pass a custom type as a generic.
This would make maintaining and reusing the code
a much cleaner and easier process.
 
K

KJ

wallge said:
Typically, the problem is that I have custom types
(records, arrays, etc) that are used as ports.
The definition of these types is done in a package.

That's the correct place for it.
This allows the various entities that use the
custom types as ports to connect to one another.
The thing is, I don't want to have to have to
keep track of ,say, 10 different packages
when putting a design together

You lost me on just what it is that you're keeping track of. In order to
use the entities and packages you simply include the files into the project
one time.
- typically
I have to edit several of the package definitions
in order to make everything agree, and if
I forget one here or there, the design won't
compile.

Well if you have to edit package definitions in order to get things to agree
then there seems to be a problem with how you're defining things in those
packages. Each package 'should' be either
- Completely stand alone (i.e. dependent on no other packages)
- Or only dependent on standard packages (i.e. like ieee.std_logic_1164,
ieee.numeric_std, etc.)
- Or at most dependent on some package of common things that you can reuse
on most any project (i.e pkg_vhd_common). The file that contains
pkg_vhd_common would be compiled first (or at least very early since it is
so 'common').

pkg_vhd_common (my name, not any standardized name) would contain things
that you build up over time as things that you frequently use from project
to project but never change (other than to add additional useful things as
you work them out).
What I really want is a kind of functionality
similar to C++ template classes, where you can
pass a custom type as a generic.

That would be handy but it's not there with VHDL. If this is the gist of
what you're getting at as far as the package edits and editing then I
certainly agree that in certain situations, a class template would be a
handy thing but you can also do a surprising amount without it.

One fairly trivial but easy example to understand just for discussion
purposes would be the simple fifo. Let's say you start by writing and
debugging the code for a fifo that works only with std_ulogic_vectors. On
some subsequent project you want to put integers, or some record type or
whatever into a fifo. Now you're stuck with having to create a new fifo
(presumably based on your already written std_ulogic_vector fifo and doing
copy/paste/edit) that handles integers or the new record type whereas if
VHDL had class template type of notation you wouldn't since the type
information would be passed in.

But now consider an approach where you define functions that convert from
your custom type into (or back from) std_ulogic_vectors. Something like
function to_std_ulogic_vector(L: my_type) return std_ulogic_vector
function from_std_ulogic_vector(L: std_ulogic_vector) return my_type

Now you can interface to your already written, debugged and working fifo
component by using these conversion functions and it is only costing you two
lines of code of typing (the declaration of a signal to use for the fifo
data output and the line that converts that signal into the type that you
really want it to be).

signal Fifo_Out_sulv: std_ulogic_vector(...);
signal Fifo_Out_my_type: my_type;
....
Fifo_Of_My_Type : my_sulv_fifo
generic map(
width => Fifo_Out_sulv'length,
...
port map(
Data_In => to_std_ulogic_vector(Fifo_In_My_Type),
Data_Out => Fifo_Out_sulv,
...
);

Fifo_Out_my_type <= from_std_logic_vector(Fifo_Out_sulv);

From a synthesis standpoint the to/from conversion functions costs 0 logic
since they are generally just fancy ways of renaming signals.

So by simply deciding on a basic type that you will generally be designing
from (i.e. std_ulogic_vector in this example) and providing functions to
convert to/from this type from any new type that you create you've created
most of the capability that could come from a class template. Note that the
proper place for these to/from conversion functions is in the package that
defines the new type not in the fifo package (in this example).

By having only one (or at most very few) basic design types you'll have very
few conversion functions that need to be created with each new type but most
important your already written and working packages won't need to expand
later on as you try to reuse them because you won't be expanding on the
basic design types that you use at the most fundamental level.

The function names that you use you should standardize on as well
(to_std_ulogic_vector and from_std_ulogic_vector in this case). All of your
new types will create functions with these same names (function overloading
is supported in VHDL). That way when you go to use these functions, the
compiler will figure out the appropriate function to use based on the
argument and return types whereas you as the designer can simply type
'to_std_ulogic_vector(Sig_Of_My_Type)' without banging your head trying to
remember what type the signal is.

There are some places where this approach breaks down a bit but maybe this
is along the lines of what you're looking for
This would make maintaining and reusing the code
a much cleaner and easier process.

I've found the approach I described above to be clean and easy to understand
and it does not incur any synthesis penalties (i.e. extra logic) either.

Kevin Jennings
 
M

Mike Treseler

wallge said:
I have to edit several of the package definitions
in order to make everything agree, and if
I forget one here or there, the design won't
compile.

I use emacs vhdl-generate-makefile and vhdl-make
to work out the compilation dependencies in project files.
KJ has covered the elimination of dependency loops.
What I really want is a kind of functionality
similar to C++ template classes, where you can
pass a custom type as a generic.

Me too. Ada has it, vhdl doesn't.
Something like KJ's functional type conversions
could be built into the language if anyone
had the time and money. However, just as it
is today, vhdl beats the verilogs and the
vendor wizards on practical abstractions
for fpga synthesis.

-- Mike Treseler
 
P

Paul Uiterlinden

wallge said:
The problem with this (as far as I can tell) is that you can't pass
types

(eg. array_type is array (3 downto 0) of unsigned(7 downto 0))

as generics.

Not that it is going to solve your problem right away, but in VHDL-200X
(VHDL-2008) you can pass types as generics. Also packages can have
generics. See http://www.synthworks.com/papers/index.htm, first paper
(Accellera VHDL 2006 Standard 3.0). Also google on VHDL-2008.
 
A

Andy

That's the correct place for it.


You lost me on just what it is that you're keeping track of. In order to
use the entities and packages you simply include the files into the project
one time.


Well if you have to edit package definitions in order to get things to agree
then there seems to be a problem with how you're defining things in those
packages. Each package 'should' be either
- Completely stand alone (i.e. dependent on no other packages)
- Or only dependent on standard packages (i.e. like ieee.std_logic_1164,
ieee.numeric_std, etc.)
- Or at most dependent on some package of common things that you can reuse
on most any project (i.e pkg_vhd_common). The file that contains
pkg_vhd_common would be compiled first (or at least very early since it is
so 'common').

pkg_vhd_common (my name, not any standardized name) would contain things
that you build up over time as things that you frequently use from project
to project but never change (other than to add additional useful things as
you work them out).


That would be handy but it's not there with VHDL. If this is the gist of
what you're getting at as far as the package edits and editing then I
certainly agree that in certain situations, a class template would be a
handy thing but you can also do a surprising amount without it.

One fairly trivial but easy example to understand just for discussion
purposes would be the simple fifo. Let's say you start by writing and
debugging the code for a fifo that works only with std_ulogic_vectors. On
some subsequent project you want to put integers, or some record type or
whatever into a fifo. Now you're stuck with having to create a new fifo
(presumably based on your already written std_ulogic_vector fifo and doing
copy/paste/edit) that handles integers or the new record type whereas if
VHDL had class template type of notation you wouldn't since the type
information would be passed in.

But now consider an approach where you define functions that convert from
your custom type into (or back from) std_ulogic_vectors. Something like
function to_std_ulogic_vector(L: my_type) return std_ulogic_vector
function from_std_ulogic_vector(L: std_ulogic_vector) return my_type

Now you can interface to your already written, debugged and working fifo
component by using these conversion functions and it is only costing you two
lines of code of typing (the declaration of a signal to use for the fifo
data output and the line that converts that signal into the type that you
really want it to be).

signal Fifo_Out_sulv: std_ulogic_vector(...);
signal Fifo_Out_my_type: my_type;
...
Fifo_Of_My_Type : my_sulv_fifo
generic map(
width => Fifo_Out_sulv'length,
...
port map(
Data_In => to_std_ulogic_vector(Fifo_In_My_Type),
Data_Out => Fifo_Out_sulv,
...
);

Fifo_Out_my_type <= from_std_logic_vector(Fifo_Out_sulv);

From a synthesis standpoint the to/from conversion functions costs 0 logic
since they are generally just fancy ways of renaming signals.

So by simply deciding on a basic type that you will generally be designing
from (i.e. std_ulogic_vector in this example) and providing functions to
convert to/from this type from any new type that you create you've created
most of the capability that could come from a class template. Note that the
proper place for these to/from conversion functions is in the package that
defines the new type not in the fifo package (in this example).

By having only one (or at most very few) basic design types you'll have very
few conversion functions that need to be created with each new type but most
important your already written and working packages won't need to expand
later on as you try to reuse them because you won't be expanding on the
basic design types that you use at the most fundamental level.

The function names that you use you should standardize on as well
(to_std_ulogic_vector and from_std_ulogic_vector in this case). All of your
new types will create functions with these same names (function overloading
is supported in VHDL). That way when you go to use these functions, the
compiler will figure out the appropriate function to use based on the
argument and return types whereas you as the designer can simply type
'to_std_ulogic_vector(Sig_Of_My_Type)' without banging your head trying to
remember what type the signal is.

There are some places where this approach breaks down a bit but maybe this
is along the lines of what you're looking for


I've found the approach I described above to be clean and easy to understand
and it does not incur any synthesis penalties (i.e. extra logic) either.

Kevin Jennings

Ahhh, type conversions on ports...

Beware of problems when the ports are of unconstrained types. In such
cases, the conversion function must return (or accept as in the case
of a conversion on an out or inout port) a constrained type. The width
of an unconstrained port is obtained from the width of the associated
actual, which is unknown in the case of a conversion function that
returns an unconstrained type.

For instance

data_in => to_slv(my_data);

to_slv() must return a specific length slv if data_in is declared as
an unconstrained slv.

For output mode ports:

to_my_type(data_out) => my_data_out;

has the same restriction, but on the argument of to_my_type(), not the
result.

Finally, for bidirectional ports:

to_my_type(data_io) => to_slv(my_data_io);

Keep in mind that type conversion functions also work in configuration
port maps.

Andy
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top