I can’t see what I’m doing wrong here at line 41. Constant aggregates are best, but … is it something to do with array aggregates?
This will, I hope, be code for a RISC-V core in an RP2350 chip.
-*- mode: compilation; default-directory: "~/Developer/FreeRTOS-Ada/rp2350/adainclude/" -*-
Compilation started at Sat Sep 7 20:10:13
gnatmake problem.adb -gnatl
gcc -c -gnatl problem.adb
GNAT 14.2.0
Copyright 1992-2024, Free Software Foundation, Inc.
Compiling: problem.adb
Source file time stamp: 2024-09-07 19:09:06
Compiled at: 2024-09-07 20:10:13
1. with System.Machine_Code;
2.
3. procedure Problem is
4.
5. package RP2350 is
6. type Bit is mod 2**1
7. with Size => 1;
8. type UInt5 is mod 2**5
9. with Size => 5;
10. type UInt11 is mod 2**11
11. with Size => 11;
12. end RP2350;
13. use RP2350;
14.
15. type Interrupt_ID is range 0 .. 45;
16.
17. procedure Enable_Interrupt (ID : Interrupt_ID);
18.
19. -- This type is subtyped for MEIEA, MEIPA, MEIFA.
20.
21. type Bit_Window_Content is array (0 .. 15) of Boolean
22. with Pack, Size => 16;
23. type CSR_Bit_Data is record
24. INDEX : RP2350.UInt5 := 0;
25. Reserved : RP2350.UInt11 := 0;
26. WINDOW : Bit_Window_Content := (others => False);
27. end record
28. with Size => 32;
29.
30. for CSR_Bit_Data use record
31. INDEX at 0 range 0 .. 4;
32. Reserved at 0 range 5 .. 15;
33. WINDOW at 0 range 16 .. 31;
34. end record;
35.
36. subtype MEIEA_Data is CSR_Bit_Data;
37.
38. procedure Enable_Interrupt (ID : Interrupt_ID)
39. is
40. ID_In_Window : constant Integer := Integer (ID mod 16);
41. Window : constant Bit_Window_Content := (ID_In_Window => True,
|
>>> error: dynamic or empty choice in aggregate must be the only choice
42. others => False);
43. Data : constant MEIEA_Data := (INDEX => UInt5 (ID / 16),
44. WINDOW => Window,
45. others => <>);
46. begin
47. System.Machine_Code.Asm
48. ("csrrs meinext, %0",
49. Inputs => MEIEA_Data'Asm_Input
50. ("r", Data),
51. Volatile => True);
52. end Enable_Interrupt;
53.
54. begin
55. null;
56. end Problem;
56 lines: 1 error
gnatmake: "problem.adb" compilation error
Compilation exited abnormally with code 4 at Sat Sep 7 20:10:13
I think it’s choking on the fact that Integer can be outside 0..15, even though you have that taken care of with the mod 16.
Could you quickly add/alter so that:
type Nybble is range 0..15 with Size => 4;
type Bit_Window_Content is array ( Nybble ) of Boolean
with Pack, Size => 16;
--…
procedure Enable_Interrupt (ID : Interrupt_ID) is
ID_In_Window : constant Nybble := Nybble (ID mod 16);
Window : constant Bit_Window_Content := (ID_In_Window => True, others => False);
--…
40. ID_In_Window : constant Integer := Integer (ID mod 16);
41. Window : constant Bit_Window_Content
42. := Bit_Window_Content'(ID_In_Window => True,
|
>>> error: dynamic or empty choice in aggregate must be the only choice
43. others => False);
I tried removing the constant … no joy.
This goes against the grain, but compiles OK, so I’ll leave determining whether there’s an error to be reported to another day! Thanks for all the suggestions.
Data : MEIEA_Data;
begin
Data.INDEX := RP2350.UInt5 (ID / 16);
Data.WINDOW (Integer (ID mod 16)) := True;
Of course, given the very short scope of this procedure, the generated code may well be just the same as would have been produced by the first try had it worked.
I think the way this hardware works (it’s RaspberryPi’s Xh3irq interrupt controller extension in the RISC-V half of the RP2350) needs a little bit more explanation by way of comments in the code.
Oh, and the CSR should have been meiea. I suppose you have to expect assembler mnemonics to be obscure.
Window : constant Bit_Window_Content :=
(for I in 0 .. 15 => (if I = ID_In_Window then True else False));
I tried it in jdoodle and got it to compile on GNAT 13.2.
I don’t know if it is a bug or intended. I looked at section 4.3.3 of the RM (ref below) but coudln’t make a solid decision on if your code is allowed or not.
The discrete_choice_list of an array_component_association is allowed to have a discrete_choice that is a nonstatic choice_expression or that is a subtype_indication or range that defines a nonstatic or null range, only if it is the single discrete_choice of its discrete_choice_list, and there is only one array_component_association in the array_aggregate.
Isn’t it that you have to drop the others? I wonder if a default_component_value would work. If that’s the case then I think I have thought it strange too.
@JC001 is correct. if you have a dynamic choice in an array aggregate, it must be the only component association in the aggregate. On the other hand, array aggregates can now be specified using “for X in ... =>” and this gives you a lot of flexibility. Several folks suggested this, the simplest being the one from @OneWingedShark (slightly adjusted here):
Window : constant Bit_Window_Content :=
(for I in Bit_Window_Content'Range => I = ID_In_Window)
I agree the rule given in RM 4.3.3(17) seems kind of arbitrary, but it is one of those rules that has been there forever, and no one has bothered to wonder whether it could be relaxed. In fact, the Ada 83 rule was slightly more limiting, and so the current rule (which dates from Ada 95) was a slight relaxation on that one. Feel free to make a suggestion on the ARG GitHub issues list that this rule be relaxed further …
What you’re doing wrong is violating a language rule. Whether the rule is necessary or could be relaxed is a different question.
For a one-dimensional array, I see two options:
procedure Non_Static_Agg is
type I16 is range 0 .. 15;
type List is array (I16) of Boolean;
procedure Try (Index : in I16);
procedure Try (Index : in I16) is
A : constant List := (List'First .. Index - 1 => False) & True & (Index + 1 .. List'Last => False);
B : List := (others => False);
begin -- Try
B (Index) := True;
end Try;
begin -- Non_Static_Agg
null;
end Non_Static_Agg;
The rule may be related to the rule for discriminant values in record aggregates. Many people are confused by
type Disc is range 1 .. 4;
type Rec (D : Disc) is record
case D is
when 1 .. 2 =>
A : Integer;
when 3 .. 4 =>
B : Float;
end case;
end record;
...
function New_Rec (D : in Disc) return Rec is
begin
case D is
when 1 .. 2 =>
return (D => D, A => -3);
...
being illegal. The value for the discriminant must be static. Instead, one can use
when 1 .. 2 =>
declare
R : Rec (D => D);
begin
R.A := -3;
return R;
end;
(an extended return can also be used). This is similar to the use of B in the array example.
If you still have that odious “they declare each register with its own array-type” then use a generic.
-- Assuming: Type Bit is new Boolean with Size => 1;
Generic
Type Index is (<>);
Type Bit_Vector is array (Index range <>) of Bit;
Function Value( Input : Index ) return Bit_Vector;
Function Value( Input : Index ) return Bit_Vector is
Begin
Return Result : Bit_Vector:= (Index'Range => False) do
Result(Input) := True;
End return;
End Value;
If you’re having to deal with anonymous types (e.g. A : Array (1..8) of Bit;) the easiest way might be overlays+generics.
If it’s just something as cumbersome as Type AX_Type is... followed by the variables, then I would use generics+overloading to provide a usable/consistent interface:
-- Gven:
Bit_Width : Constant 32;
Type Generic_Register of Array(1..Bit_Width) of Bit;
Type Register_AX is new Generic_Register;
Type Register_BX is new Generic_Register;
AX : Register_AX;
BX : Register_BX;
-- Use something like:
Type Bit_Index is range 1..Bit_Width; -- Mirroring the register's.
Generic
Type Register_Type is Array(1..Bit_Width) of Bit;
R : in out Register;
Procedure Set_Bit( X : Bit_Index );
Procedure Set_Bit( X : Bit_Index ) is
Actual_Index : Constant := R'First + (X-Bit_Index'First);
Begin
R( Actual_Index ):= True;
End Set_Bit;
--...same for unset;