Understanding bounds of concatenated arrays in Ada 2012 - arrays

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;

Related

Constrain the indexes for SEQUENCE OF types in ASN.1

Is it possible in ASN.1 to add a constraint for the available indeces of a SEQUENCE OF type?
Something along the lines of this
MyArray ::= SEQUENCE (1..10) OF INTEGER -- MyArray has 10 elments indexed from 1-10
Array2 ::= SEQUENCE (-5..5) OF INTEGER -- Array2 has 11 elments indexed from -5-5
I tried to get it out of the ASN.1 book from Dubuisson, but I am not completely sure I understand the grammar description correctly.
My current understanding is that it is possible to create constraints on the sizes of a SEQUENCE OF, but it is not foressen to have any influence on the indexing behavior. I assume this is left to the implementation language to define?
Is this correct?
In ASN.1 there is no concept of "index" for a SEQUENCE OF. Your assignments above are both invalid.
You can specify a constraint on the number of elements of a SEQUENCE OF. For example, if you write
A1 ::= SEQUENCE (SIZE (10)) OF INTEGER
you are restricting the number of elements to 10. Any value of this SEQUENCE-OF type will have to have exactly 10 elements.
If you write
A2 ::= SEQUENCE (SIZE (1..10)) OF INTEGER
any value of this SEQUENCE-OF type will have to have at least 1 element and at most 10 elements. In this case, the number of elements is restricted to a range of sizes rather than to a fixed size.
You can also write more complex constraints like the following:
A3 ::= SEQUENCE (SIZE (1 | 4 | 6..MAX)) OF INTEGER
In this case, any value of this SEQUENCE-OF type must have 1, 4, or 6 or more elements. For example, the values {2} and {-5, 5, 1, -3} are valid values, whereas {1, 2} is not.
You cannot use a negative integer in a size constraint because the integer you use specifies a number of elements, not an index value.

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.

Assign the values for an array in Ada

I'm approaching the Ada language. I wrote this simple program that loops over an array and increments every single value, but the compiler gives me an error of type:
hello.adb:8:07: left hand side of assignment must be a variable
The program in question is this:
with Ada.Text_IO;
procedure hello is
type myArrayDefinition is array (1 .. 10) of integer;
myArray : constant myArrayDefinition := (1 => 3, others => 2);
begin
for A in 1 .. 10 loop
myArray(A) := myArray(A) + 1;
end loop;
end hello;
Could anyone help me to understand the problem?
You can't modify the value of a constant. There are no special problems related to modifying the values of arrays.

Copy arrays of same type and size but different index type in Ada

I am learning Ada at the moment and the current lesson is on arrays. Consider the following program;
procedure Main is
type T_Bool is array (Boolean) of Integer;
type T_Intg is array (Integer range 1 .. 2) of Integer;
A1 : T_Bool := (others => 0);
A2 : T_Intg := (others => 0);
begin
-- None of these statements work
A2 := A1;
A2 (1 .. 2) := A1 (false .. true);
A2 := A1 (Boolean'First .. Boolean'Last);
end Main;
According to the Adacore university tutor, it is possible to copy values from one array to another as long as the lengths are the same. In the code snippet, why can you not assign arrays with the same size but different indexing methods, despite the fact that the length is the same?
What is the correct way to copy this across? Is it a case of looping through all indexes in the Boolean type range and copying the corresponding array index across or is there another clever way of doing it?
The tutorial says, on slide 3, that "all arrays are (doubly) typed", which means that - as usual for different types - you can't assign between your two array types; so yes, you need a loop.
There are "clever" ways using, for example, unchecked conversion; but, really, don't go there!
It is possible to copy arrays without any explicit conversion if they are of the same type and length with only the starting index differing:
procedure Test is
type T is array (Positive range <>) of Integer;
A : T(1 .. 10);
B : T(21 .. 30);
begin
A := B;
end Test;
Otherwise it is possible to assign arrays of different type with explicit conversion, but apart from having the same dimensionality (and more, see ARM 4.6) the index types also have to be convertible. Boolean is an enumeration type which is not convertible to Integer.
Explicit conversion with convertible index types would look like this:
procedure Main is
type TMod is mod 10000;
type T_Mod is array (TMod range 5000 .. 5001) of Integer;
type T_Intg is array (Integer range 1 .. 2) of Integer;
A1 : T_Mod := (others => 0);
A2 : T_Intg := (others => 0);
begin
A2 := T_Intg(A1);
end Main;
So yes, it would look like you need to copy elements in a loop in your example.
Thanks for your answers guys, it is definitely useful information. It goes even deeper than I thought. In the quiz part of the tutorial, I learned that even this is an error
procedure Main is
type T1 is array (Integer range 1 .. 10) of Integer;
type T2 is array (Integer range 1 .. 10) of Integer;
A1 : T1;
A2 : T2;
begin
A1 := (others => 0);
-- Cannot do this
A2 := A1;
end Main;
Types A2 and A1 may well be defined in the same way, but Ada considers them unrelated and therefore incompatible. It would be the equivalent in C++ of doing
typedef int MyType1;
typedef int MyType2;
MyType1 MyVar1;
MyType2 MyVar2;
// Error - Cannot assign differing types,
// despite them being semantically the same
MyVar2 = MyVar1;
In this particular case (or when there are only few values for index), it is possible to assign in once with an aggregate.
A2 := (1 => A1 (False),
2 => A1 (True));
The complete example:
https://gcc.godbolt.org/z/josscfPzh

For loop is ambiguous

I'm using Visual Basic 2008EE and I have a problem with this loop:
If x = CType("new", Primitive) Then
TextWindow.Write("How many new users would you like to add? ")
k = TextWindow.ReadNumber()
For mt = 1 To k
NewUserEntry()
Next
and i get this error:
"type of 'mt' is ambigious because the loop bounds and the step clause do not convert to the same type"
I appreciate any help.
The return type of ReadNumber (or more accurately, the type of k variable) is probably not an Integer. When the compiler wants to infer the type of the mt, it fails since k, which is specified as the loop bound has one type (probably something like Double) and the loop step (implicitly the integer constant 1) has the type Integer. The compiler will not automatically assume the type of mt since the two don't match.
For mt As Integer = 1 To k
NewUserEntry()
Next

Resources