Can you make an array of types in VHDL? - vhdl

Vivado Simulation cannot support unconstrained types which have a signed component to them.
i.e.
type A is array (natural range <>) of signed;
I have been using this in a design where type A is used in port declarations as I wish to have a parallel design which I control through a generic as well as the current stage word length e.g.
port (
inputdata : A(0 to number_of_parallel_generic-1)(stage_wordlength_generic-1 downto 0)
);
As I use the type A with many variations of the generics controling them e.g. 4 wide arrays with 16 wordlengths and other variations (often controled by a for generate loop)
for i in 0 to length_of_generate_statement-1 generate
signal example_signal : A(0 to 3)(stage_wordlength_generic + i - 1 downto 0);
begin
<functional code>
end generate;
This sort of code would allow me to gain bit growth from sequential sections of my archetecture - e.g. from an addition.
Now... getting to the question at hand.
One way I could get round this rather than initiating a signal with a forever changing generate statement could actually be in the creation of an "array of types".
Lend me your eyes this is written in a not quite vhdl way but hopefully you can see what Im trying to do.
type my_arr_of_types is array(0 to length_of_array-1) of type;
for i in 0 to length_of_array-1 generate
my_arr_of_types(i) <= <type declaration with some dependance on i>;
end generate;
Hopefully you can see what I am trying to do.
This would allow you to then call an element of the my_arr_of_types which itself is a type to then assign to a signal/variable.
i.e.
signal my_sig : my_arr_of_types(n);
*Where n is any valid index of the array.
Obviously this is not allowed in VHDL or any simulation tool. But can anyone see a potential solution to my problem?
Remember I use most of these types on port statements so any solution has to fit within the limitations of the port declarations.

Using two dimensional arrays as a solution:
Package
library ieee;
use ieee.numeric_std.all;
package utilities is
type T_SLM is array(natural range <>, natural range <>) of std_logic;
end package;
Entity
Now you can use this type in a port declaration together with two generic parameters. As sizes are now known in the architecture, you can create your used defined type of signed values and you can use either generate statements or a function to convert from the T_SLM to myArray type.
library ieee;
use ieee.numeric_std.all;
library myLib;
use myLib.utilities.all;
entity foo is
generic (
number_of_parallel : natural;
stage_wordlength : natural
);
port (
Input : T_SLM(0 to number_of_parallel - 1, stage_wordlength - 1 downto 0)
);
end entity;
architecture a of foo is
type myArray is array (natural range <>) of signed(Input'range(2));
function convert(matrix : T_SLM) return myArray is
variable result : myArray(matrix'range(1));
begin
for i in matrix'range(1) loop
for k in matrix'range(2) loop
result(i)(j) := matrix(i, j);
end loop;
end loop;
return result;
end function;
signal InputData1 : myArray(Input'range(1));
signal InputData2 : myArray(Input'range(1));
begin
genInput: for i in Input'range(1) generate
genInput: for j in Input'range(2) generate
InputData1(i)(j) <= Input(i, j);
end generate;
end generate;
InputData2 <= convert(Input);
end architecture;
Many helper functions like this have been implemented in the PoC Library in package PoC.vectors.

Related

What happens when I provide a function with the parent of the subtype argument it's expecting?

While trying to figure out the specifics of the shift_right function from the numeric_std package I noticed that the count argument is of the subtype NATURAL:
function shift_right(ARG: UNSIGNED; COUNT: NATURAL) return UNSIGNED is
begin
if (ARG'length<1) then return NAU; end if;
return UNSIGNED(XSRL(STD_LOGIC_VECTOR(ARG),COUNT));
end;
However when calling the function I can also provide an INTEGER which in contrast to NATURAL can hold a negative number.
Example of calling code that succesfully compiles:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity ExampleCode is
port
(
clk : in std_logic;
input : in signed(15 downto 0);
shift : in signed(3 downto 0);
output : out signed(15 downto 0)
);
end entity;
architecture rtl of ExampleCode is
begin
ProcessExample : process(clk)
begin
if (rising_edge(clk)) then
output <= shift_right(input, to_integer(shift));
end if;
end process;
end rtl;
The numeric_std package shows that if you use to_integer with a signed argument that it returns an integer:
function TO_INTEGER ( ARG: SIGNED) return INTEGER;
My questions are;
Does VHDL always allow parents of subtypes to be provided as arguments to functions?
When it does allow a parent type, how does it resolve the imposed constraints of the subtype?
The other answer is very detailed, but I think goes down a bit of a rabbit hole, when your specific questions can be answered more succinctly. I have answered from a perspecitve of what will practically happen in a real tool that you might use, as opposed to trying to re-interpret the language standard.
An important feature of a subtype is that there is automatic 'conversion'+ to and from the parent type. The example below clearly shows this with an enumerated type. The same automatic conversion would be invoked when passing a parent-type parameter to a function that expects the sub-type.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity e is
end;
architecture a of e is
type r is (A, B, C, D);
subtype rs is r range A to C;
signal x1 : r := B;
signal x2 : r := D;
signal xs : rs;
begin
process
begin
xs <= x1; -- Fine.
wait for 1 ns;
xs <= x2; -- Run-time error, "Value 3 is out of range 0 to 2".
wait;
end process;
end;
Does VHDL always allow parents of subtypes to be provided as arguments to functions?
From the above, yes, it does, but in many tools, you will get an error if the automatic type conversion cannot succeed, as it obviously cannot in the second assignment in the example. Note that if x2 was a constant, a tool could work out that the conversion is not going to be possible, and thrown up a compile-time error then instead.
The same applies with natural and integer; since natural is defined as subtype natural is integer range 0 to integer'high, natural in a sense is an integer, so automatic 'conversion' is simple and reasonable as long as the integer is not outside the natural's range.
When it does allow a parent type, how does it resolve the imposed constraints of the subtype?
Whether standardised or not, a particular tool might implement this in a variety of ways, so you might see different behavior for out-of-range parent-typed values with different tools.
For example, when I tried with ModelSim, it appears that its conversion from integer to natural simply copies the value, meaning that shift_right will surprisingly work with a negative value in that tool, if the shift amount integer is not a constant (at least for version 10.7e).
Obviously it is not sensible to rely on a particular behavior, but regardless of behavior, using a sub type can offer you more protection than just using base types throughout a design.
+ It's not really conversion in VHDL, but if you've used pretty much any other language, this is how you will tend to refer to it.

Custom Type as VHDL 2008 Generic

I want to create a custom type in my generic section of my entity using VHDL-2008. However I get an error immediately in Modelsim with this code. The error is:
** Error: C:/Projects/source/My_Mux.vhd(35): near "is": expecting ';' or ')'
Note that Line 35 is the type t_Array below:
entity My_Mux is
generic (
g_MUX_INPUTS : integer := 2;
type t_Array is array (0 to g_MUX_INPUTS-1) of std_logic_vector(7 downto 0)
);
port (
i_Select : in std_logic_vector(1 downto 0);
i_Mux_Data : in t_Array;
o_Data : out std_logic_vector(7 downto 0)
);
end entity My_Mux;
architecture RTL of My_Mux is
begin
o_Data <= i_Mux_Data(0) when i_Select = "00" else i_Mux_Data(1);
end architecture RTL;
I looked into creating a special function that I define in my generic portion of my code. But that requires that I overload the function in the instantiating module, which I really did not want to have to do, it seems needlessly complicated. If I could create a custom type in the generic it would solve my problem. Possible using VHDL-2008?
How would you expect to have type compatibility between the formal and actual if they declaration of a type were actually made in a generic declaration?
Each declaration in VHDL is unique, not by name but by declaration occurrence. What declaration the name references depends on scope and visibility. Both (all) places a name is used have to be able to reach the same declaration.
How a generic type is declared is found in IEEE Std 1076-2008 6.5.3 Interface type declarations:
An interface type declaration declares an interface type that appears as a generic of a design entity, a component, a block, a package, or a subprogram.
interface_type_declaration ::=
interface_incomplete_type_declaration
interface_incomplete_type_declaration ::= type identifier
An interface type provides a means for the environment to determine a type to be used for objects in a particular portion of a description. The set of values and applicable operations for an interface type may be determined by an associated subtype in the environment. The manner in which such associations are made is described in 6.5.7.
And the important thing to note is that is an incomplete type declaration, where the actual specifies a preexisting type with a subtype constraint (6.5.6.2):
The subtype denoted by a generic type is specified by the corresponding actual in a generic association list. It is an error if no such actual is specified for a given formal generic type (either because the formal generic is unassociated or because the actual is open).
Because that association is with a previously declared type there is little difference with doing the same thing the -1993 way:
library ieee;
use ieee.std_logic_1164.all;
package my_package is
type my_array is array (natural range <>) of std_logic_vector(7 downto 0);
end package;
library ieee;
use ieee.std_logic_1164.all;
use work.my_package.all;
entity My_Mux is
generic (
g_MUX_INPUTS: integer := 2
--type t_Array is array (0 to g_MUX_INPUTS-1) of
-- std_logic_vector(7 downto 0)
);
port (
i_Select: in std_logic_vector(1 downto 0);
-- i_Mux_Data: in t_Array;
i_Mux_Data: in my_array (0 to g_MUX_INPUTS - 1);
o_Data : out std_logic_vector(7 downto 0)
);
end entity My_Mux;
architecture RTL of My_Mux is
begin
o_Data <= i_Mux_Data(0) when i_Select = "00" else i_Mux_Data(1);
end architecture RTL;
There's an added package that has a type declaration my_array which is an unbound (partially constrained) multidimensional array type.
This allows the use of the package my_package to specify the type of the actual:
library ieee;
use ieee.std_logic_1164.all;
use work.my_package.all;
entity my_mux_tb is
end entity;
architecture foo of my_mux_tb is
constant MUX_INPUTS: natural := 2;
signal i_Select: std_logic_vector (1 downto 0);
signal i_Mux_Data: my_array (0 to MUX_INPUTS -1);
signal o_Data: std_logic_vector(7 downto 0);
begin
DUT:
entity work.My_mux
generic map (
g_MUX_INPUTS => MUX_INPUTS
)
port map (
i_Select => i_Select,
i_Mux_Data => i_Mux_Data,
o_Data => o_Data
);
end architecture;
The two examples above analyzed in order, elaborate and the testbench simulates (while doing nothing particular interesting besides telling us the subtype constraint is passed on the port actual).
The custom type would be required to be accessible to both the component or entity instantiation and the place the port actual is declared.
Using a generic type would allow you to remove the my_package use clause from the my_mux context clause, relying on the actual association instead.
You can also bind the type at elaboration time without switching the package (or relying on package instantiation in -2008 with it's own generics).

VHDL: How to declare a variable width generic [duplicate]

This question already has answers here:
How to use generic parameters that depend on other generic parameters for entities?
(2 answers)
Closed 6 years ago.
I want to create a VHDL entity with a one generic that changes the width of another generic.
entity lfsr_n is
generic (
WIDTH : integer := 32; -- counter width
POLYNOMIAL : std_logic_vector (WIDTH-1 downto 0) := "1000_0000_0000_0000_0000_0000_0110_0010"
);
Unfortunately, it seems I can't reference an earlier defined generic later in the generic list. Active-HDL gives the following errors:
Error: COMP96_0300: modules/m3_test_load/lfsr_n.vhd : (26, 45): Cannot reference "WIDTH" until the interface list is complete.
Error: COMP96_0077: modules/m3_test_load/lfsr_n.vhd : (26, 66): Undefined type of expression. Expected type 'STD_LOGIC_VECTOR'.
One workaround would be to make POLYNOMIAL a port. But it properly should be a generic since since the value is constant at elaboration time. I know that if I apply a constant to the port, it will synthesize the way I want and optimize the constants values into the module, but I'd like to find someway to make it a generic. Any suggestions how to do this?
If you want the POLYNOMIAL parameter to remain a generic you can specify it as an unconstrained array. You can also dispense with the WIDTH parameter by replacing all references by POLYNOMIAL'range, POLYNOMIAL'length-1 downto 0, or POLYNOMIAL'length as needed.
entity lfsr_n is
generic (
POLYNOMIAL : std_logic_vector := X"FFAA55BB"
);
port (
-- Vector with copied range (defaults to ascending from 0)
state : out std_logic_vector(POLYNOMIAL'range);
-- Vector with forced descending range
state2 : out std_logic_vector(POLYNOMIAL'length-1 downto 0)
);
end entity;
Unconstrained arrays are a powerful feature that help simplify code by implicitly controlling widths rather than needing a dedicated generic parameter. When used effectively they reduce the number of hard-coded array sizes in your source resulting in naturally resizable logic. You can freely change the POLYNOMIAL generic to another value with a different length and the rest of your logic should adapt without any additional effort.
There's an entity declarative part following any generic and any port declaration:
library ieee;
use ieee.std_logic_1164.all;
entity lfsr_n is
generic (
WIDTH: integer := 32 -- counter width
);
port (
foo: integer
);
constant POLYNOMIAL: std_logic_vector (WIDTH-1 downto 0)
:= B"1000_0000_0000_0000_0000_0000_0110_0010";
end entity;
architecture foo of lfsr_n is
begin
end architecture;
This analyzes and elaborates showing the generic is properly used.
You could also note that the literal you assign to the std_logic_vector doesn't adapt to changing WIDTH. I would have thought these '1's stand for tap off locations and you'd expect these could change from one LFSR length to another.
You could convey the 'WIDTH' in the polynomial constant:
library ieee;
use ieee.std_logic_1164.all;
entity lfsr_n is
generic (
-- WIDTH: integer := 32; -- counter width
POLYNOMIAL: std_logic_vector :=
B"1000_0000_0000_0000_0000_0000_0110_0010"
);
port (
foo: integer
);
-- constant POLYNOMIAL: std_logic_vector (WIDTH-1 downto 0)
-- := B"1000_0000_0000_0000_0000_0000_0110_0010";
end entity;
architecture foo of lfsr_n is
signal shft_register: std_logic_vector (0 to POLYNOMIAL'LENGTH-1);
begin
end architecture;
Or:
architecture foo of lfsr_n is
-- signal shft_register: std_logic_vector (0 to POLYNOMIAL'LENGTH-1);
-- or
signal shift_register: std_logic_vector(POLYNOMIAL'RANGE);

Use a generic to determine (de)mux size in VHDL?

I want to use a generic 'p' to define how many outputs a demux will have. Input and all outputs are 1 bit. The outputs, control, and input can be something simple like:
signal control : std_logic_vector(log 2 p downto 0); -- I can use a generic for the log2..
signal input : std_logic;
signal outputs : std_logic_vector(p-1 downto 0);
But what would the mux implementation code be? Is it even possible?
No generics required:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity demux is
port(
control : in unsigned;
input : in std_logic;
outputs : out std_logic_vector
);
end entity demux;
architecture rtl of demux is
-- Check size of input vectors
assert 2**control'length = outputs'length
report "outputs length must be 2**control length"
severity failure;
-- actually do the demuxing - this will cause feedback latches to be inferred
outputs(to_integer(unsigned(control)) <= input;
end architecture;
(Untested, just typed in off the top of my head...)
This will infer latches though - is that what you want?
You need to feed log_p as generic and compute p as you go.
library ieee;
use ieee.std_logic_1164.all;
entity demux is
generic (
log_p: integer);
port(
control : in std_logic_vector(log_p downto 0);
input :in std_logic;
outputs : out std_logic_vector(2**log_p - 1 downto 0)
);
end entity demux;
You need to pass both the number of outputs and the size of the control array as generics, unless you are always using powers of two.
Outside of your (de)mux module (ie: when you instantiate), you can use code to calculate the number of bits for the control bus. I have a function in a common package I use to initialize various configuration constants and generics that get passed to code similar to your (de)mux application:
-- Calculate the number of bits required to represent a given value
function NumBits(val : integer) return integer is
variable result : integer;
begin
if val=0 then
result := 0;
else
result := natural(ceil(log2(real(val))));
end if;
return result;
end;
...which allows you to do things like:
constant NumOut : integer := 17;
signal CtrlBus : std_logic_vector(NumBits(NumOut)-1 downto 0);
my_mux : demux
generic map (
NumOut => NumOut,
NumCtrl => NumBits(NumOut) )
port map (
control => CtrlBus,
...
...

How to read data from rom_type in VHDL?

How can I read data from rom_type?
entity my_rom is
port(
addr: in std_logic_vector(3 downto 0);
data: out std_logic_vector(0 to 7)
);
end my_rom;
architecture a of my_rom is
type rom_type is array (0 to 7) of std_logic_vector(0 to 7);
constant R1_ROM: rom_type :=
(
-- data
);
begin
data <= R1_rom(conv_integer(addr));
end a;
You are using conv_integer, which is not part of raw VHDL... it's in a library. However, you don't want to use it - it's from a non-standard library.
Instead use ieee.numeric_std.all; is what you need before your entity. Then use to_integer(unsigned(addr)) to index the ROM. Better still, pass the address in as an unsigned vector, or even directly as an integer.
Try to get out of the habit of using std_logic_vector (which is just a bag of bits) to represent numbers, and use the well-defined numerical types.
Or use Verilog, which doesn't care :)
Myself, I prefer VHDL's strong-typing to keep me from daft foot-shooting...

Resources