"Generics" in VHDL Package

  • Thread starter Charles Steinkuehler
  • Start date
C

Charles Steinkuehler

Guest
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I'm trying to do something with type definitions in a package, and it
seems like I need something like generics, but they don't seem to be
supported in packages.

The setting:

In my package, I define an enumerated global configuration type which is
used to specifiy which 'flavor' the design compiles to (ie: controls
number of active DMA channels, buffer sizes, etc). The top-level design
file passes the configuration type down the physical design tree as a
generic parameter, and functions in the package are used to control
generate statements, interface widths, etc. All this works fine.

The problem:

I'm trying to define some array subtypes in the package that depend on
which 'flavor' of chip is being built (a buffer-pointer address type,
which is a subtype of unsigned with it's length dependent on the
particular configuration being built).

ie:

package my_Pkg is
type conf_T is (BabyBear, MamaBear, PapaBear);

type SettingType_T is (DMA_PBUF_SIZE, DMA_PBUF_BITS);

function get_param (config : conf_T;
setting : SettingType_T) return natural;

subtype Buf_Addr_T is unsigned(12 downto 0);
type DMA_Buf_IF_T is record
req : std_logic;
ack : std_logic;
done : std_logic;
ptr : Buf_Addr_T;
end record DMA_Buf_IF_T;

end package my_Pkg;

....except I need to be able to configure the length of the Buf_Addr_T
subtype "dynamically" (at compile time) based on the selected conf_T type.

I could simply use my existing configuration functions to control the
declaration of unsigned (instead of Buf_Addr_T) types where needed, but
that would leave me without the DMA_Buf_IF_T type (which doesn't like to
compile with ptr as an unconstrained array).

The question:

Is there any way to declare a record type that contains an array who's
length depends on the environment (ie: a generic or similar)? I tried
breaking the above code into two packages, one with the get_param
function and one with the Buf_Addr_T and DMA_Buf_IF_T type declarations,
but there's no way (that I've found) to pass my compile-time conf_T
value to the package (or to do something similar).

- --
Charles Steinkuehler
cstein@newtek.com

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2 (MingW32)

iD8DBQFEahhQenk4xp+mH40RAufCAJ9hd2hZbaaJWdn2pm+txl0N/D4v3wCdH2sh
yy9Rz0mbSBTyUqNSabX69No=
=KQo0
-----END PGP SIGNATURE-----
 
Charles Steinkuehler wrote:

The question:
Is there any way to declare a record type that contains an array who's
length depends on the environment (ie: a generic or similar)?
No, but keep in mind that it's the process that
uses the package. The package doesn't know and
can't care which of its types is in use.

I tried
breaking the above code into two packages, one with the get_param
function and one with the Buf_Addr_T and DMA_Buf_IF_T type declarations,
but there's no way (that I've found) to pass my compile-time conf_T
value to the package (or to do something similar).
The process passes nothing to the package.
The process *uses* the declarations it contains.
The process can declare its own variables or
constants using any of the types in the package.
I can't work with types directly.
I use the types as a template for local constants or registers
in the process. For example:

Suppose that I have a generic_c = 0, 1 or 2
for BabyBear, MamaBear, PapaBear

With your package, I could do things like:

type bear_vec_t is array (conf_t) of unsigned(7 downto 0);
constant bear_choices_c : bear_vec_t := (x"ff", x"f7", x"42");
subtype generic_t is natural range conf_t'pos(BabyBear)
to conf_t'pos(PapaBear);

constant which_bear_c : conf_t := conf_t'val(generic_c);
constant this_bear_vec_c : unsigned := bear_choices_c(which_bear_c);

-- Mike Treseler
 
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Mike Treseler wrote:
Charles Steinkuehler wrote:

The question:
Is there any way to declare a record type that contains an array who's
length depends on the environment (ie: a generic or similar)?

No, but keep in mind that it's the process that
uses the package. The package doesn't know and
can't care which of its types is in use.

I tried
breaking the above code into two packages, one with the get_param
function and one with the Buf_Addr_T and DMA_Buf_IF_T type declarations,
but there's no way (that I've found) to pass my compile-time conf_T
value to the package (or to do something similar).

The process passes nothing to the package.
The process *uses* the declarations it contains.
The process can declare its own variables or
constants using any of the types in the package.
I can't work with types directly.
I use the types as a template for local constants or registers
in the process. For example:

Suppose that I have a generic_c = 0, 1 or 2
for BabyBear, MamaBear, PapaBear

With your package, I could do things like:

type bear_vec_t is array (conf_t) of unsigned(7 downto 0);
constant bear_choices_c : bear_vec_t := (x"ff", x"f7", x"42");
subtype generic_t is natural range conf_t'pos(BabyBear)
to conf_t'pos(PapaBear);

constant which_bear_c : conf_t := conf_t'val(generic_c);
constant this_bear_vec_c : unsigned := bear_choices_c(which_bear_c);
A very interesting approach I hadn't thought of...thanks!

I assume the extra complexity (array indexing) would be generally
ignored by the synthesis tool since it all winds up being globally
static (ie: known at compile-time), right?

Also, why go through the trouble of numerically indexing the array,
wouldn't directly assigning a conf_t type to which_bear_c (or in my
case, passing it as a conf_t typed generic) work as well? What benefit
is there to converting to and from the integer generic_c type?

/me - wanders off to try this in modelsim and Quartus...

- --
Charles Steinkuehler
cstein@newtek.com

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2 (MingW32)

iD8DBQFEak7wenk4xp+mH40RAl8VAJ92M1mVF4b78zwNChTQigKne97jDQCggJPS
cCLzZg8Mv1TqBKvAQovEIQs=
=OxOx
-----END PGP SIGNATURE-----
 
Charles Steinkuehler wrote:

A very interesting approach I hadn't thought of...thanks!
You are welcome.
It was an interesting question.

I assume the extra complexity (array indexing) would be generally
ignored by the synthesis tool since it all winds up being globally
static (ie: known at compile-time), right?
Ignored in the sense of using up gates and flops.
Not ignored in the sense of type checking
and making things line up right automatically.

Also, why go through the trouble of numerically indexing the array,
wouldn't directly assigning a conf_t type to which_bear_c (or in my
case, passing it as a conf_t typed generic) work as well? What benefit
is there to converting to and from the integer generic_c type?
The intended benefit was a clear example.
No benefit at all for your design since
your entity is using a package anyway.

/me - wanders off to try this in modelsim and Quartus...
Tell how Quartus does with enumerated generics.

-- Mike Treseler
 
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Mike Treseler wrote:

Tell how Quartus does with enumerated generics.
I haven't tested your constant array idea with Quartus yet, but Quartus
does just fine with enumerated generics (at least in 5.0 and 5.1, which
is what I've been using).

I have different top-level design files (and corresponding project files
for Quartus and ModelSim) that pass a conf_T to the underlying logic as
a generic, ie:

in BabyBear.vhd:

use work.my_Pkg.vhd
...

constant config : conf_T := BabyBear;
...

component BackEnd_C is
generic (
config : conf_T);
port (
...

be : BackEnd_C
generic map (
config => config)
port map (
...

The code in the other top-level files is pretty much identical (with
appropriate values assigned to config, of course!), and mainly differs
in external I/O (different configurations have different numbers and
types of external interfaces).

I have a config generic on most of the lower-level design units, and
just pass the top-level provided config down the heirarchy. The
lower-level design units use the config parameter and functions provided
in my_Pkg to control various design parameters (ie: number of active DMA
channels, number of input/output video streams, etc). I have had no
problems with Quartus compiling this, and don't expect any wierdness
caused by the addition of a constant array with an enumerated index (but
won't know for sure until I try it, of course...saddly, it's looking
like that won't happen until tomorrow).

- --
Charles Steinkuehler
cstein@newtek.com

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2 (MingW32)

iD8DBQFEals+enk4xp+mH40RAorWAJ9CScLPdFW2vq1ZCF4GbHrCbDl9VACeMyh5
+/qSkVZjcDfJTT499mlKHBg=
=q3iX
-----END PGP SIGNATURE-----
 
Mike,

Maybe I'm missing something here but I thought the basic problem was that
the number of bits in the 'Buf_Addr_T' subtype needed to vary as a function
of a top level generic and I don't see how your post addresses that
problem....except for the very first statement where you say "No, but ...."

Presumably the 'DMA_Buf_IF_T' record type is used to communicate between
various entities and is therefore defined in the package. Since the 'ptr'
element of that type is defined to be 'Buf_Addr_T is unsigned(12 downto 0);'
but the actual range '12 downto 0' that is needed is design dependent so I
*thought* what was needed was for the

BabyBear to somehow have a range like '10 downto 0'
MamaBear to somehow have a range like '11 downto 0'
PapaBear to somehow have a range like '12 downto 0'

Also presuming that the reason for wanting to vary the range is because than
the 'BabyBear' design would fit into a 22V10, the 'MamaBear' design would
fit into a Stratix and the 'PapaBear' design would fit into a Virtex-5. (I
realize that this is probably not really the case but also that there
probably is some actual advantage to 'BabyBear' being smaller than
'PapaBear' otherwise why post the question?) So the real motivation here is
to have a single common code base that can be easily 'configured' in some
sense to use in different design applications.

If the 'ptr' element happened to work it's way out to the top level of the
design than it's likely that you can simply leave 'ptr' defined as needed
for the largest application and the fitter would be able to optomize out the
unneeded bits (i.e. ptr elements 10 and 9 for BabyBear, bit 11 for
MamaBear).

If 'ptr' doesn't make it's way out to the top then I think the fitter would
likely have a hard time spotting the optomization and 'BabyBear' would
likely end up being implemented the same way as 'PapaBear' and would require
use of the Virtex-5 instead of the 22V10 so it would be a rather pricey way
to keep the 'common code base'.

I *think* about the only way to handle this would be to have separate
'BabyBear.vhd', 'MamaBear.vhd' and 'PapaBear.vhd' source files. In each of
those file would be the same package containing the needed subtype/record
definitions where you modify it as required for the 'BabyBear', 'MamaBear'
and 'PapaBear' designs and then include only one of these source files in
the targetted build.

subtype Buf_Addr_T is unsigned(12 downto 0); <- Use '10 downto 0' for
'BabyBear', etc.
type DMA_Buf_IF_T is record
req : std_logic;
ack : std_logic;
done : std_logic;
ptr : Buf_Addr_T;
end record DMA_Buf_IF_T;

Either that, or like I said, I totally missed something ;)

KJ
 
KJ wrote:

Maybe I'm missing something here but I thought the basic problem was that
the number of bits in the 'Buf_Addr_T' subtype needed to vary as a function
of a top level generic and I don't see how your post addresses that
problem....except for the very first statement where you say "No, but ...."
I said no because the only direct solution to the problem
of generic width types in a package is
to move the declarations to the process
or architecture where the
generic dimensions are in scope.

If the separate package is a requirement,
then the only solution I can imagine
is indirect. Some generic has to index
some sort of prepackaged constant array.

....
If the 'ptr' element happened to work it's way out to the top level of the
design than it's likely that you can simply leave 'ptr' defined as needed
for the largest application and the fitter would be able to optomize out the
unneeded bits (i.e. ptr elements 10 and 9 for BabyBear, bit 11 for
MamaBear).
I think that could work also.
I'm sure Charles will sort it out.
Thanks for the new ideas.

-- Mike Treseler
 
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

KJ wrote:
Mike,

Maybe I'm missing something here but I thought the basic problem was that
the number of bits in the 'Buf_Addr_T' subtype needed to vary as a function
of a top level generic and I don't see how your post addresses that
problem....except for the very first statement where you say "No, but ...."

Presumably the 'DMA_Buf_IF_T' record type is used to communicate between
various entities and is therefore defined in the package. Since the 'ptr'
element of that type is defined to be 'Buf_Addr_T is unsigned(12 downto 0);'
but the actual range '12 downto 0' that is needed is design dependent so I
*thought* what was needed was for the

BabyBear to somehow have a range like '10 downto 0'
MamaBear to somehow have a range like '11 downto 0'
PapaBear to somehow have a range like '12 downto 0'

Also presuming that the reason for wanting to vary the range is because than
the 'BabyBear' design would fit into a 22V10, the 'MamaBear' design would
fit into a Stratix and the 'PapaBear' design would fit into a Virtex-5. (I
realize that this is probably not really the case but also that there
probably is some actual advantage to 'BabyBear' being smaller than
'PapaBear' otherwise why post the question?) So the real motivation here is
to have a single common code base that can be easily 'configured' in some
sense to use in different design applications.

If the 'ptr' element happened to work it's way out to the top level of the
design than it's likely that you can simply leave 'ptr' defined as needed
for the largest application and the fitter would be able to optomize out the
unneeded bits (i.e. ptr elements 10 and 9 for BabyBear, bit 11 for
MamaBear).

If 'ptr' doesn't make it's way out to the top then I think the fitter would
likely have a hard time spotting the optomization and 'BabyBear' would
likely end up being implemented the same way as 'PapaBear' and would require
use of the Virtex-5 instead of the 22V10 so it would be a rather pricey way
to keep the 'common code base'.

I *think* about the only way to handle this would be to have separate
'BabyBear.vhd', 'MamaBear.vhd' and 'PapaBear.vhd' source files. In each of
those file would be the same package containing the needed subtype/record
definitions where you modify it as required for the 'BabyBear', 'MamaBear'
and 'PapaBear' designs and then include only one of these source files in
the targetted build.

subtype Buf_Addr_T is unsigned(12 downto 0); <- Use '10 downto 0' for
'BabyBear', etc.
type DMA_Buf_IF_T is record
req : std_logic;
ack : std_logic;
done : std_logic;
ptr : Buf_Addr_T;
end record DMA_Buf_IF_T;

Either that, or like I said, I totally missed something ;)
No, you're absolutely correct. The technique of using constant arrays
indexed by an enumerated configuration specifier is simply a more
elegant way of doing what I've got already (with functions where I pass
a config specifier, a desired constant name, and get a value back). I
was temporarily blinded by the elegance (or at least code-reduction)
this solution has over my function based solution.

I still don't have a way to change the values in the package (when used
in type declarations) based on the desired configuration, because the
package has no way of knowing what configuration I want to use.

I really need something like configuration generics, which look like
they're being added to VHDL 200x (at least based on my review of the
proposed floating point library), but I'm not holding my breath until I
can use this feature with Quartus and Modelsim.

As you mention, I think the only way around this problem is to have
separate package files with the required types defined hard-coded (or at
least with the configuration specifier hard-coded...then the type sizes
can be crafted from globally static constant/function expansions).

What I'll probably do is split my package so the stuff that controls the
configuration is in one package, and things like records and type
definintions that rely on a particular configuration are in a separate
package. I'll then have to select individual source files for the
second package, depending on which configuration I'm compiling for.

At least if I design the second package correctly, the only difference
between the configuration-specific files should be one line: The line
that specifies the desired configuration (as a constant). Then once
VHDL 200x actually becomes real enough, I can loose the multiple files
and use a package generic...in the mean-time, however, it's about
getting product out the door!

- --
Charles Steinkuehler
cstein@newtek.com

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2 (MingW32)

iD8DBQFEazwyenk4xp+mH40RAi1rAJ40t8xSYsQoQpSIFJHUqBfMWetfKwCg7x2f
M7fj+d2uYgxdxYr0fVnKlDA=
=Ytx8
-----END PGP SIGNATURE-----
 
Darn, I was actually hoping that I had just completely missed something
too. What Mike posted is good stuff but I thought maybe I was having a
senior moment or something and had totally misunderstood something.

As you mention, I think the only way around this problem is to have
separate package files with the required types defined hard-coded (or at
least with the configuration specifier hard-coded...then the type sizes
can be crafted from globally static constant/function expansions).
I'm not getting how you'd be able to have a configuration specifier
help in changing the number of bits in 'ptr' though (assuming that's
what you meant by "then the type sizes can be crafted from globally
static constant/function expansions"

What I'll probably do is split my package so the stuff that controls the
configuration is in one package, and things like records and type
definintions that rely on a particular configuration are in a separate
package. I'll then have to select individual source files for the
second package, depending on which configuration I'm compiling for.
Dang...if only VHDL had the C style #include...sigh ;)

KJ
 
I said no because the only direct solution to the problem
of generic width types in a package is
to move the declarations to the process
or architecture where the
generic dimensions are in scope.
Twas actually hoping that I had completely misunderstood and that if I
studied it more all would've been clear...alas, the new trick that was
leaned was not the new trick that I was hoping for.

KJ
 
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

KJ wrote:
Darn, I was actually hoping that I had just completely missed something
too. What Mike posted is good stuff but I thought maybe I was having a
senior moment or something and had totally misunderstood something.

As you mention, I think the only way around this problem is to have
separate package files with the required types defined hard-coded (or at
least with the configuration specifier hard-coded...then the type sizes
can be crafted from globally static constant/function expansions).

I'm not getting how you'd be able to have a configuration specifier
help in changing the number of bits in 'ptr' though (assuming that's
what you meant by "then the type sizes can be crafted from globally
static constant/function expansions"
In my_pkg.vhd:

type conf_T is (BabyBear, MamaBear, PapaBear);

type bear_config_t is array (conf_t) of natural;
constant Buf_Addr_Bits_c : bear_config_t := (10, 11, 12);

....and in my_pkg2.vhd:

constant config : conf_T := BabyBear;

subtype Buf_Addr_T is unsigned( Buf_Addr_Bits_c(config)-1 downto 0);

type DMA_Buf_IF_T is record
req : std_logic;
ack : std_logic;
done : std_logic;
ptr : Buf_Addr_T;
end record DMA_Buf_IF_T;

So...I can make the second package 'smart' in that it uses constants
defined in the first package, but there's no way to override the
required hardcoded:

constant config : conf_T := <desired configuration>;

....which must be defined at compile time. That means each configuration
has to have it's own custom my_Pkg2.vhd file with at least that one line
changed. :(

What I was getting at with my globally static comment:
The upper range for Buf_Addr_T (ie: Buf_Addr_Bits_c(config)-1) is
globally static, meaning you don't know the exact value when compiling
just my_Pkg2, but when the entire design is analyzed (and the values
from my_pkg are pulled in), the expression degenerates into a constant
value (10, 11, or 12, depending on the value assigned to config) because
Buf_Addr_Bits_c, config, and the -1 are constants.

I'll probably make a 'master' my_Pkg2 file and use make to generate the
derivations...I know from experience I'm not good at keeping multiple
nearly identical files in sync manually! :)

....and I already have a makefile that generates synthesis versions of
files from *_syn.vhd versions that include tags indicating which lines
should be uncommented or deleted to make the synthesis version, extracts
documentation from tagged comments, etc.

NOTE: The above has been tested in ModelSim, but not yet tested in
Quartus (although I don't expect any issues...I'm doing similar things
already).

Sure will be nice when generic configuration values can be directly
passed to a package...

- --
Charles Steinkuehler
cstein@newtek.com

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2 (MingW32)

iD8DBQFEa47tenk4xp+mH40RAm5BAJsEWUagaP/zC44zNtw+3S5vGCMhxACfXWrB
GDro+nLmlfkdoxVFeNJh+Ho=
=ocFu
-----END PGP SIGNATURE-----
 

Welcome to EDABoard.com

Sponsor

Back
Top