"Generics" in VHDL Package

Discussion in 'VHDL' started by Charles Steinkuehler, May 16, 2006.

  1. -----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


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

    iD8DBQFEahhQenk4xp+mH40RAufCAJ9hd2hZbaaJWdn2pm+txl0N/D4v3wCdH2sh
    yy9Rz0mbSBTyUqNSabX69No=
    =KQo0
    -----END PGP SIGNATURE-----
     
    Charles Steinkuehler, May 16, 2006
    #1
    1. Advertising

  2. 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
     
    Mike Treseler, May 16, 2006
    #2
    1. Advertising

  3. -----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


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

    iD8DBQFEak7wenk4xp+mH40RAl8VAJ92M1mVF4b78zwNChTQigKne97jDQCggJPS
    cCLzZg8Mv1TqBKvAQovEIQs=
    =OxOx
    -----END PGP SIGNATURE-----
     
    Charles Steinkuehler, May 16, 2006
    #3
  4. 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
     
    Mike Treseler, May 16, 2006
    #4
  5. -----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


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

    iD8DBQFEals+enk4xp+mH40RAorWAJ9CScLPdFW2vq1ZCF4GbHrCbDl9VACeMyh5
    +/qSkVZjcDfJTT499mlKHBg=
    =q3iX
    -----END PGP SIGNATURE-----
     
    Charles Steinkuehler, May 17, 2006
    #5
  6. Charles Steinkuehler

    KJ Guest

    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, May 17, 2006
    #6
  7. 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
     
    Mike Treseler, May 17, 2006
    #7
  8. -----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


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

    iD8DBQFEazwyenk4xp+mH40RAi1rAJ40t8xSYsQoQpSIFJHUqBfMWetfKwCg7x2f
    M7fj+d2uYgxdxYr0fVnKlDA=
    =Ytx8
    -----END PGP SIGNATURE-----
     
    Charles Steinkuehler, May 17, 2006
    #8
  9. Charles Steinkuehler

    KJ Guest

    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
     
    KJ, May 17, 2006
    #9
  10. Charles Steinkuehler

    KJ Guest

    > 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
     
    KJ, May 17, 2006
    #10
  11. -----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


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

    iD8DBQFEa47tenk4xp+mH40RAm5BAJsEWUagaP/zC44zNtw+3S5vGCMhxACfXWrB
    GDro+nLmlfkdoxVFeNJh+Ho=
    =ocFu
    -----END PGP SIGNATURE-----
     
    Charles Steinkuehler, May 17, 2006
    #11
  12. Charles Steinkuehler

    davidrupe

    Joined:
    Apr 29, 2008
    Messages:
    1
    I am no expert here...

    But there is a very good work-around that I have been using for a few years now.

    Just do this...

    pkg.vhd

    constant MAX_WIDTH := 16;
    subtype MAX_RANGE : is natural range MAX_WIDTH-1 downto 0;

    type XT is record
    field : slv(MAX_WIDTH-1 downto 0);
    -- or field : slv(MAX_RANGE);

    end

    code.vhd

    use pkg.vhd

    generic (
    LOCAL_WIDTH : integer := 8
    );
    port (
    dataIn : in XT;
    dataOut : out XT
    );

    arch BEH of ?? is

    subtype LOCAL_RANGE is natural range LOCAL_WIDTH-1 downto 0;

    begin
    dataOut(LOCAL_WIDTH-1 downto 0) <= dataIn(LOCAL_WIDTH-1 downto 0);
    or
    dataOut(LOCAL_RANGE) <= dataIn(LOCAL_RANGE);
    end

    simulation will show additional initialized bits unless you drive them to zero
    synthesis will give you warnings saying you aren't driving bits

    code works...

    8)

    Later,
    David Rupe
    FPGA Product Manager
    BittWare
     
    davidrupe, Apr 29, 2008
    #12
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    2
    Views:
    8,721
    Jim Lewis
    Mar 21, 2006
  2. Juergen Berchtel
    Replies:
    1
    Views:
    6,031
    John C. Bollinger
    May 20, 2005
  3. afd
    Replies:
    1
    Views:
    8,414
    Colin Paul Gloster
    Mar 23, 2007
  4. Kevin Neilson

    Passing Generics into a Package File

    Kevin Neilson, May 21, 2008, in forum: VHDL
    Replies:
    3
    Views:
    487
  5. Soul
    Replies:
    0
    Views:
    532
Loading...

Share This Page