Reputation: 452
I'm trying to understand generic programming in VHDL and I've written some simple code to be able to do so. My goal is to be able to write a generic design (generic in the sense that certain things like input/output size of an entity are not specified) and then instantiate it in a top-level module and specify the design constraints at instantiation. This way, I can write a single design to satisfy many different parameter constraints depending on the needs of the user. Otherwise, I would need to write a separate design for each file where only a few modifications are made in each. An obvious use case for this sort of flexibility would be in creating a library.
These two files illustrate what I'm trying to achieve:
-- array_adder.vhd
--
-- Computes and outputs the pointwise addition of two arrays.
library ieee;
use ieee.numeric_std.all;
package array_adder_pkg is
generic (array_size : integer;
precision_size : integer
);
type array_type is array (0 to array_size-1) of signed(precision_size-1 downto 0);
component array_adder is
port (a : in array_type;
b : in array_type;
c : out array_type
);
end component;
end package;
-----main code:-----------------------------------------------------------------
library ieee;
use ieee.numeric_std.all;
--------------------------------------------------------------------------------
entity array_adder is
port (a : in array_type;
b : in array_type;
c : out array_type
);
end entity;
--------------------------------------------------------------------------------
architecture fpga of array_adder is
begin
comp: for i in 0 to array_size-1 generate
c(i) <= a(i) + b(i);
end generate;
end architecture;
and
-- top_level.vhd
--------------------------------------------------------------------------------
package small_pkg is new work.array_adder_pkg
generic map (array_size => 3,
precision_size => 5
);
package large_pkg is new work.array_adder_pkg
generic map (array_size => 15,
precision_size => 9
);
use work.small_pkg.all;
use work.large_pkg.all;
library ieee;
use ieee.numeric_std.all;
--------------------------------------------------------------------------------
entity top_level is
end entity;
--------------------------------------------------------------------------------
architecture fpga of top_level is
signal small_ina : work.small_pkg.array_type :=
(to_signed(1, work.small_pkg.precision_size),
to_signed(2, work.small_pkg.precision_size),
to_signed(3, work.small_pkg.precision_size));
signal small_inb : work.small_pkg.array_type :=
(to_signed(4, work.small_pkg.precision_size),
to_signed(5, work.small_pkg.precision_size),
to_signed(6, work.small_pkg.precision_size));
signal small_out : work.small_pkg.array_type;
signal large_ina : work.large_pkg.array_type :=
(to_signed(100, work.large_pkg.precision_size),
to_signed(110, work.large_pkg.precision_size),
to_signed(120, work.large_pkg.precision_size));
signal large_inb : work.large_pkg.array_type :=
(to_signed(50, work.large_pkg.precision_size),
to_signed(30, work.large_pkg.precision_size),
to_signed(80, work.large_pkg.precision_size));
signal large_out : work.large_pkg.array_type;
begin
small_array_adder: work.small_pkg.array_adder
port map (a => small_ina,
b => small_inb,
c => small_out);
large_array_adder: work.large_pkg.array_adder
port map (a => large_ina,
b => large_inb,
c => large_out);
end architecture;
This first set doesn't compile because array_adder
doesn't know what array_type
is. In ModelSim 10.4b, I get (vcom-1136) Unknown identifier "array_type".
That's not surprising, since I never instantiated the array_adder_pkg
.
So, I tried to instantiate a default package in array_adder.vhd
, which is mostly the same, but I'll include it to be complete. top_level.vhd
stays the same.
-- array_adder2.vhd
--
-- Computes and outputs the pointwise addition of two arrays.
library ieee;
use ieee.numeric_std.all;
package array_adder_pkg is
generic (array_size : integer;
precision_size : integer
);
type array_type is array (0 to array_size-1) of signed(precision_size-1 downto 0);
component array_adder is
port (a : in array_type;
b : in array_type;
c : out array_type
);
end component;
end package;
-----main code:-----------------------------------------------------------------
package default_array_adder_pkg is new work.array_adder_pkg
generic map (array_size => 3,
precision_size => 7
);
use work.default_array_adder_pkg.all;
library ieee;
use ieee.numeric_std.all;
--------------------------------------------------------------------------------
entity array_adder is
port (a : in array_type;
b : in array_type;
c : out array_type
);
end entity;
--------------------------------------------------------------------------------
architecture fpga of array_adder is
begin
comp: for i in 0 to array_size-1 generate
c(i) <= a(i) + b(i);
end generate;
end architecture;
Now both files compile, but when I try to simulate top_level.vhd
in ModelSim I get the error Fatal: (vsim-3714) At array depth 1, array lengths do not match. Left is 15 (0 to 14). Right is 3 (0 to 2).
which seems to indicate that ModelSim is having trouble with instantiation sizes different than the default. To test this further, I removed the second package instantiation and all code associated with it in top_level.vhd
(i.e. all the large_pkg
stuff and the component instantiation associated with it). I'll spare you the mostly redundant code this time. Again, this compiles as expected, but when I attempt to simulate it I get Fatal: (vsim-3807) Types do not match between component and entity for port "a".
The only way I can actually get this to work is to use the default package in the top-level design and forego two different sized instantiations, but this defeats the point of a generic design since I will need to write a separate design file for each set of parameter specifications.
Is there another way to do this that I'm missing? Or, is this not something that VHDL currently supports?
Upvotes: 3
Views: 2243
Reputation: 11261
You were almost there, but needed to assign default values to the component in your package.
Then VHDL-2008 allows for unconstrained arrays, which can help you here.
See this example:
library ieee;
use ieee.numeric_std.all;
package array_pkg is
type signed_array is array (natural range <>) of signed;
end package;
use work.array_pkg.signed_array;
package array_adder_pkg is
generic(
array_size : positive;
precision_size : positive
);
subtype array_type is signed_array(0 to array_size-1)(precision_size-1 downto 0);
component array_adder is
generic(
array_size : positive := array_size;
precision_size : positive := precision_size
);
port (
a : in signed_array(0 to array_size-1)(precision_size-1 downto 0);
b : in signed_array(0 to array_size-1)(precision_size-1 downto 0);
c : out signed_array(0 to array_size-1)(precision_size-1 downto 0)
);
end component;
end package;
use work.array_pkg.signed_array;
entity array_adder is
generic(
array_size : positive;
precision_size : positive
);
port (
a : in signed_array(0 to array_size-1)(precision_size-1 downto 0);
b : in signed_array(0 to array_size-1)(precision_size-1 downto 0);
c : out signed_array(0 to array_size-1)(precision_size-1 downto 0)
);
end entity;
library ieee;
architecture fpga of array_adder is
use ieee.numeric_std.all;
begin
comp: for i in 0 to array_size-1 generate
c(i) <= a(i) + b(i);
end generate;
end architecture;
entity top_level is end entity;
package small_pkg is new work.array_adder_pkg
generic map (array_size => 3,
precision_size => 5
);
package large_pkg is new work.array_adder_pkg
generic map (array_size => 3,
precision_size => 9
);
library ieee;
architecture fpga of top_level is
use ieee.numeric_std.all;
signal small_ina : work.small_pkg.array_type :=
(to_signed(1, work.small_pkg.precision_size),
to_signed(2, work.small_pkg.precision_size),
to_signed(3, work.small_pkg.precision_size));
signal small_inb : work.small_pkg.array_type :=
(to_signed(4, work.small_pkg.precision_size),
to_signed(5, work.small_pkg.precision_size),
to_signed(6, work.small_pkg.precision_size));
signal small_out : work.small_pkg.array_type;
signal large_ina : work.large_pkg.array_type :=
(to_signed(100, work.large_pkg.precision_size),
to_signed(110, work.large_pkg.precision_size),
to_signed(120, work.large_pkg.precision_size));
signal large_inb : work.large_pkg.array_type :=
(to_signed(50, work.large_pkg.precision_size),
to_signed(30, work.large_pkg.precision_size),
to_signed(80, work.large_pkg.precision_size));
signal large_out : work.large_pkg.array_type;
begin
small_array_adder: work.small_pkg.array_adder
port map (a => small_ina,
b => small_inb,
c => small_out);
large_array_adder: work.large_pkg.array_adder
port map (a => large_ina,
b => large_inb,
c => large_out);
end architecture;
EDIT: I wanted to add that you can also generate the specialized packages local to your architecture. That way they will not be compiled into your library. Example:
entity top_level is end entity;
library ieee;
architecture fpga of top_level is
use ieee.numeric_std.all;
package small_pkg is new work.array_adder_pkg
generic map (array_size => 3,
precision_size => 5
);
package large_pkg is new work.array_adder_pkg
generic map (array_size => 3,
precision_size => 9
);
signal small_ina : small_pkg.array_type :=
(to_signed(1, small_pkg.precision_size),
to_signed(2, small_pkg.precision_size),
to_signed(3, small_pkg.precision_size));
signal small_inb : small_pkg.array_type :=
(to_signed(4, small_pkg.precision_size),
to_signed(5, small_pkg.precision_size),
to_signed(6, small_pkg.precision_size));
signal small_out : small_pkg.array_type;
signal large_ina : large_pkg.array_type :=
(to_signed(100, large_pkg.precision_size),
to_signed(110, large_pkg.precision_size),
to_signed(120, large_pkg.precision_size));
signal large_inb : large_pkg.array_type :=
(to_signed(50, large_pkg.precision_size),
to_signed(30, large_pkg.precision_size),
to_signed(80, large_pkg.precision_size));
signal large_out : large_pkg.array_type;
begin
small_array_adder: small_pkg.array_adder
port map (a => small_ina,
b => small_inb,
c => small_out);
large_array_adder: large_pkg.array_adder
port map (a => large_ina,
b => large_inb,
c => large_out);
end architecture;
Upvotes: 2