How to partially constrain VHDL arrays (elements)? - arrays

I am trying to use unconstrained arrays in a VHDL-2008 testbench, but Modelsim (2020.1) gives an error if I try to declare a file type.
This simplified example reproduces the error:
entity tb is
end entity;
architecture arch of tb is
type array_t is array(natural range<>) of bit_vector;
subtype sub_t is array_t(open)(15 downto 0);
type file_t is file of sub_t;
begin
end architecture;
vcom -2008 tb.vhd
# ** Error: tb.vhd(8): Cannot define file of an array whose element subtype is not fully
constrained.
I am confused by this error message because, as far as I understand, the element subtype is fully constrained. I had a look in VHDL 2008: Just the New Stuff which says (in Section 3.1.2):
If we want to define a partially constrained subtype, specifying index
ranges for the elements but leaving the index range at the top level
unspecified, we can use the reserved word open in place of an index
range
So why do I get this error? As a sanity check, the following compiles without error:
entity tb is
end entity;
architecture arch of tb is
type array_t is array(natural range<>) of bit_vector(15 downto 0);
subtype sub_t is array_t;
type file_t is file of sub_t;
begin
end architecture;
I don't really understand what the exact differences are between the declarations of sub_t in each case (and why one should work whereas the other fails).
Thanks for the help!

Related

Register map implementation in VHDL

In my design I am trying to create a register map, which can be used by separate components in a flexible way. For example, let's say I create a following register map type:
package regmap_package is
type regmap_t is array(natural range <>) of std_logic_vector(7 downto 0);
end package regmap_package;
So this would be my register map with x amount of 8 bit registers. In my top entity I then declare the total size of the register map:
signal regs : regmap_t(0 to 15);
So in this example I would have 16 x 8 bit register map. Here comes my problem: Lets say I want to create some sub-components. Each of that component, would only need some portion of these register to operate with.
For example component1 would need the register from address 0 to address 7, and components 2 registers from address 8 to address 15. How would the entity declaration for such component look like and how to pass the portion of the whole register array to it? Will the components operate on its own, local addressing schemes (from 0 to 7 each)?
You can use an enumeration to declare register names:
type RegisterNames is (Command, Status, VendorID, DeviceID, Error);
Now you can create your register map:
type RegisterType is array(RegisterNames range <>) of std_logic_vector(7 downto 0);
By using range <>, you can create any range of that type.
From here you can generate your register map:
signal RegisterMap : RegisterType(RegisterNames);
You can slice this register map as follows:
signal SubRegisters : RegisterType(VendorID to DeviceID);
SubRegister <= RegisterMap(SubRegister'range);
Answers to comments
Can a package be created with the global labels available to be used as indexes?
Yes, you can define the described enumeration type in a package. Thus, the register names and the register type itself is available to be used as ports and in higher level entities.
Unfortunately, VHDL-2008 has a broken support for incomplete generic types. You could pass an enumeration type into a package and then pass that instantiated package into an entity implementing a generic register interface e.g. for AXI4 Lite, but that generic type comes with no operations. Thus, you have no operators and no attributes of a generic type available.
How would it look like?
package GenericRegisterPackage is
generic (
constant ShortName : string; -- e.g. PWM
type RegisterNames;
constant AXI4_AddressBits : positive;
constant AXI4_DataBits : positive
);
subtype RegisterType is std_logic_vector(AXI4_DataBits - 1 downto 0);
-- THE FOLLOWING LINE IS NOT SUPPORTED in VHDL-2008
type RegisterFile is array(RegisterNames range <>) of RegisterType;
end package;
entity GenericAXI4LiteRegister is
generic (
package GenericRegisterPackage
);
port (
Clock : in std_logic;
-- ...
);
end entity;
Usage:
architecture rtl of Controller is
type RegisterNames is (Command, Status, Error, Frequency);
package RegisterPackage is new work.GenericAXI4LiteRegister
generic map (
ShortName => "PWM Controller",
RegisterNames => RegisterNames,
AXI4_AddressBits => 8,
AXI4_DataBits => 32
);
begin
reg: entity work.GenericAXI4LiteRegister
generic map (
GenericRegisterPackage => RegisterPackage
)
port map (
Clock => Clock,
-- ...
);
end architecture;
This misconception of VHDL-2008 will be fixed in VHDL-2018. The following line from the package's incomplete generic type:
type RegisterNames;
will become this:
type RegisterNames is ();
...denoting, it is an enumeration type. Thus, it's a discrete type and can be used as an index in arrays.
Also, is specific index setting (natural number) possible here?
Yes, you can convert enumeration literals to indices. Every discrete type (including enumerations) has position numbers for all its values.
constant position : natural := RegisterNames'pos(VendorID);
will return 2. The positions starts at 0 for the left-most enumeration literal in the declaration. Every following literal to the right gets a positions incremented by 1.
The reverse operation from position number (natural) to an enumeration literal it the attribute 'val(5), which returns Error.

How do you initialize an array with only one element, without hardcoding the first index?

I am trying to pass an array of Unbounded_String to a function, and I don't care about the range of the index, as the function is going to loop over each element.
The (element1, element2) syntax automatically starts at the first index value in the range, then increments for the second value given, which works fine for more than one value. However, for a single value, this cannot be used as the parentheses are considered superfluous.
This code shows the error messages for each of the attempts I have made. (1) works, but (2), the preferable syntax for passing a single-element array, does not. (3) works, and is given as an answer to this similar question. However, this hardcodes the first index of the range into the calling side; if the String_Array implementation changes, all the call-sites have to be changed, even though they don't care about the index values used.
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
procedure Main is
function "+"(S: String) return Ada.Strings.Unbounded.Unbounded_String
renames Ada.Strings.Unbounded.To_Unbounded_String;
type String_Array is array (Positive range <>) of Unbounded_String;
procedure Foo(input : in String_Array) is
begin
null;
end Foo;
begin
Foo((+"one", +"two")); --(1)
--Foo((+"only")); --(2) positional aggregate cannot have one component
Foo((1 => +"only")); --(3)
--Foo((String_Array'First => +"only")); --(4) prefix for "First" attribute must be constrained array
--Foo((String_Array'Range => +"only")); --(5) prefix for "Range" attribute must be constrained array
--Foo((String_Array'Range'First => +"only")); --(6) range attribute cannot be used in expression
--Foo((String_Array'Range'Type_Class'First => +"only")); --(7) range attribute cannot be used in expression
end Main;
What you want (2) is indeed impossible as it could be mistaken for a parenthesized expression (see http://www.adaic.org/resources/add_content/standards/12aarm/html/AA-4-3-3.html note 10).
If you really want to avoid expression (3) for the reasons you stated, as workaround, you could define a function to handle the one-element array case:
function Singleton_String_Array (S: String) return String_Array is ((1 => + S));
-- one element call
Foo(Singleton_String_Array ("only"));
It reuse your expression (3) but the first index hardcoding is no longer done on call site.
You can also overload your foo function to handle the special one-element case:
procedure Process_String (input : in Ada.Strings.Unbounded.Unbounded_String) is
begin
null;
end Process_String;
procedure Foo(input : in String_Array) is
begin
for string of input loop
Process_String (string);
end loop;
end Foo;
procedure Foo(input : in Ada.Strings.Unbounded.Unbounded_String) is
begin
Process_String (input);
end Foo;
-- One element call
Foo(+"only");
The short answer is that all array objects must be constrained, which means callers usually have to decide on the array bounds.
However, you know the index type, and could do
Foo((Positive'First => +"only"));
which doesn't really answer your question, since someone may still fiddle with the array range, and there's not really any guard against that.
Adding a new subtype as the range may be a viable solution, though:
subtype String_Array_Range is Positive;
type String_Array is array (String_Array_Range range <>) of Unbounded_String;
...
Foo((String_Array_Range'First => +"only"));
Any fiddling can now be done on the String_Array_Range subtype without affecting any callers. But there's still no guarantee against evil programmers changing the index type of the array itself...
type String_Array is array (Positive range <>) of Unbounded_String;
Declares a type of array but doesn't provide the size.
Remember that an array has a static size.
So String_Array'First and String_Array'Range don't match to anything.
If you declared
type my_String_Array is String_Array(1 .. 35);
my_arr : my_String_Array
Then my_arr'First denotes 1 and my_arr'Range denotes 1..35.
As long as you don't put a contraint on the type, you won't have access to these attributes.

VHDL array concatenation of varying types

I am doing some undergraduate research that involved some VHDL coding, something i have virtually no experience with. I am running into some issues and hoping for some help:
I need to read in a signal (array of "std_logic_vectors"), and perform a logical shift on it. My thought was to concatenate an array of 0's with an array that contains the relevant portion of the input. Here is what i've done:
Library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use work.SignalGen.all;
entity Shifter is
Port ( xsig_in : in SignalGen;
shift : in integer;
clk : in STD_LOGIC;
x_out1 : out SignalGen;
x_out2 : out SignalGen);
end Shifter;
architecture Behavioral of Shifter is
type x_in_type is array (0 to (1024-shift) of std_logic_vector;
type zeroes_type is array(0 to shift) of std_logic_vector;
signal x_shifted: SignalGen;
signal x_in : x_in_type := xsig_in(1024 downto shift); -- This is line 37
signal zeroes : zeroes_type := (others => (others => '0'));
begin
process(clk)
begin
x_shifted <= zeroes & x_in; -- This is line 49
end process;
end Behavioral;
Its important to know what type SignalGen is, so here is my declaraion:
type SignalGen is array (0 to 1024) of STD_LOGIC_VECTOR(15 downto 0);
So as you can see, I've created a type "zeroes_type", which is the length of the desired shift. It is used to generate a signal "zeroes" filled with zeroes. Also x_in is of type x_in_type, which is the length of the input signal minus the shift amount, and is initialized to be the signal minus the values shifted out.
When i try to concatenate them together, I get these errors:
Line 37: Indexed name is not a x_in_type
Line 49: found '0' definitions of operator "&", cannot determine exact overloaded matching definition for "&"
The line numbers are included in comments in the code posting. I've been tinkering with this segment for hours now and nothing i've tried to do can fix it.
If anyone can shed some light on the matter, or give me a better way to do what I am trying to do, I would be extremely appreciative! Thanks!
I think you were a long way from where you needed to be. This will not be an efficient implementation in terms of area: you will have 16,000+ flip-flops. However, this solution ought to be closer to where you need to be:
Library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use work.SignalGen.ALL;
entity Shifter is
Port ( xsig_in : in SignalGen;
shift : in integer;
clk : in STD_LOGIC;
x_out1 : out SignalGen;
x_out2 : out SignalGen);
end Shifter;
architecture Behavioral of Shifter is
begin
process(clk)
variable V : SignalGen;
begin
if rising_edge(clk) then
V := xsig_in;
for I in 1 to xsig_in'RIGHT loop
if I <= shift then
V := "0000000000000000" & V(0 to xsig_in'RIGHT-1);
end if;
end loop;
x_out1 <= V;
end if;
end process;
end Behavioral;
http://www.edaplayground.com/x/8AQ
Variables, loops and concatenation are often useful when doing shifting as you can see in my example. You can jump out of the loop (using exit) when you're finished shifting, but my experience you might get slightly more logic doing that, ie:
if I <= shift then
V := "0000000000000000" & V(0 to xsig_in'RIGHT-1);
else
exit;
end if;
There are many errors in your code. Whilst, I'm sure you will learn something from me just posting the answer, here are some explanations about the errors you are seeing:
type x_in_type is array (0 to (1024-shift)) of std_logic_vector;
You were missing a right hand parenthesis and this line relies on you using VHDL 2008, because the inner dimension is unconstrained. But even then, you cannot define a type based on a non-static quantity (shift), anyway.
signal x_in : x_in_type := xsig_in(1024 downto shift);
With you array constrained as it is, you need to skip over the outer dimension to constrain the inner one. In VHDL 2008 you can skip over a constrained dimension like this:
signal x_in : x_in_type := xsig_in(open)(1024 downto shift);

Is it possible to create dynamically 2d arrays in Ada without using 'declare'?

I need to create a number of functions that deal with matrices of arbitrary size. I am familiar with the declare syntax used here, but this is for an university assignment, and my professor told me that using 'declare' there is overkill. I can't find anything relevant online, any help?
Basically I'm looking to get the matrix size via keyboard and then work with the resulting matrix, I'm stuck with the declare
Currently I have:
type myMatrix is array (Natural range <>, Natural range <>) of Integer;
type myVector is array (Natural range <>) of Integer;
And I use it as:
procedure Lab1 is
begin
declare A, B: myVector(1..5):=
(3, 14, 15, 92, 6);
which doesn't allow to specify the size at runtime, and as:
declare
int1:Integer:=generate_random_number(50)+2;
int3:Integer:=generate_random_number(50)+2; -- +2 so we don't get length/size 0 or 1
X, Y:myVector(1..int1):=(others=>generate_random_number(20));
MT:myMatrix(1..int1, 1..int3):=(others =>(others=>generate_random_number(10))); -- 'others' used for all the unmentioned elements, http://rosettacode.org/wiki/Array_Initialization
MS:myMatrix(1..int3, 1..int1):=(others =>(others=>generate_random_number(10)));
F3_result:myMatrix(1..int1, 1..int1);
begin
F3_result:=F3(X, Y, MT, MS);
end;
which uses a declare block. I might need to use the resulting array later on, and as I understand here F3 is a local variable, and therefore can't be reused?
Are there any other ways?
I agree with your teacher that putting a declare just after a begin will in general show that the declare block is unneeded (the only exception I can think of is because you want to handle exceptions that might arise as part of the declaration of the variables within the enclosing subprogram).
So you can just remove both begin and declare at the start of your subprogram, and that should work the same.

Understanding bounds of concatenated arrays in Ada 2012

I am reading Programming in Ada 2012 by John Barnes. In section 8.6 he discusses array concatenation and the rules for array bounds, in particular:
The lower bound of the result depends on whether the underlying array type is
constrained or not. If it is unconstrained...then the lower bound is
that of the left operand...[otherwise] the lower bound is that of the array index subtype.
Then in the exercises for 8.6, question 7 is as follows (I've added the answers given on the website PDF inside the [] ):
Given
type TC is array (1..10) of Integer;
type TU is array (Natural range <>) of Integer;
AC: TC;
AU: TU(1..10);
What are the bounds of:
(a) AC(6..10) & AC(1..5) [1..10]
(b) AC(6) & AC(7..10) & AC(1..5) [1..10]
(c) AU(6..10)& AU(1..5) [6..15]
(d) AU(6) & AU(7..10) & AU(1..5) [0..9]
The answers to a and b made sense to me since the AC array is based on a constrained type we just use the bounds of the index. I would think the answers to c and should both be 6..15 since the underlying type is unconstrained the leftmost operand AU(6) or AU(6..10) will determine the starting bounds. Then I tried coding it up as shown below to better understand and all four show the bounds as 1..10. Is my code wrong, are the answers wrong or is the description in the text wrong? (BTW, I also coded with new array variables and made the assignments to those but the results are the same).
type TC is array (1..10) of Integer;
type TU is array (Natural range <>) of Integer;
AC: TC;
AU: TU(1..10);
begin
AC := AC(6..10) & AC(1..5);
Tio.Put("7a) Constrained type starting with 6..10 ");
Iio.Put(AC'First); Iio.Put(AC'Last); Tio.New_Line;
Tio.Put ("7b) Constrained type starting with 6 ");
AC := AC(6) & AC(7..10) & AC(1..5); -- 7.b
Iio.Put(AC'First); Iio.Put(AC'Last); Tio.New_Line;
Tio.Put ("7c) Unconstrained type starting with 6..10");
AU := AU(6..10) & AU(1..5);
Iio.Put(AU'First); Iio.Put(AU'Last); Tio.New_Line;
Tio.Put_Line("Answer keys says 6..15");
Tio.Put ("7d) Unconstrained type starting with 6 ");
AU := AU(6) & AU(7..10)& AU(1..5);
Iio.Put(AU'First); Iio.Put(AU'Last); Tio.New_Line;
Tio.Put_Line("Answer key says 0..9 - Why not 6..15???");
(Tio and Iio are just renames of the std Ada io packages for text and integer)
When run, this code produces the following console output:
E:\Google Drive\SW_DEV\Ada\Sample\obj\hello
7a) Constrained type starting with 6..10 1 10
7b) Constrained type starting with 6 1 10
7c) Unconstrained type starting with 6..10 1 10
Answer keys says 6..15
7d) Unconstrained type starting with 6 1 10
Answer key says 0..9 - Why not 6..15???
Your AU is defined to start with lower bound of 1 (an anonymous array subtype with range 1..10). That will never change.
For the unconstrained results (c and d) you should assign to a new variable, like this:
declare
AU_c : TU := AU(6..10) & AU(1..5);
AU_d : TU := AU(6) & AU(7..10)& AU(1..5);
begin
-- your Put/Put_Lines here
end;

Resources