[Help] Array indexing, variant records and the nRF58240

Dear community,

I am trying to program a nRF58240 board that I have. I saw that there is support for it in Alire and I am using the crate to work with it GitHub - damaki/nrf52-runtimes: Ada/SPARK run-times for nRF52 SoCs (props to @damaki)

TL;DR: I am getting an unexpected warning/exception by the compiler and I do not understand why and I do not know how to debug it.

I want to try out a simple led blinking example for my board. For that I created the following code example:

with Interfaces.NRF52.GPIO;
use  Interfaces.NRF52.GPIO;

procedure Test is
   LED_Pin : constant := 15;
   procedure Set_LED_Output
     with Inline
   is
   begin
      -- P0_Periph.PIN_CNF(15) := (DIR => Output, others => <>);
      -- P0_Periph.DIR := (As_Array => True,
      --                   Arr      => (LED_Pin => Output, others => <>));   
      P0_Periph.DIR := (As_Array => True,
                        Arr      => (others => Output));   
      P1_Periph.DIR := (As_Array => True,
                        Arr      => (others => Output));   
   end Set_LED_Output;

   procedure Wait
     with Inline
   is
      Ticks : constant := 1_000_000;
      Counter : Integer := 0;
   begin
      while Counter /= Ticks loop
         Counter := Counter + 1;
      end loop;
   end Wait;
   
   type Mode is (Off, On);
   procedure Set_LED (Dir : Mode)
     with Inline
   is
   begin
      -- P0_Periph.OUT_k := (As_Array => True,
      --                     Arr      => (LED_Pin => (if Dir = Off then Low else High), others => <>));   
      P0_Periph.OUT_k := (As_Array => True,
                          Arr      => (others => (if Dir = Off then Low else High)));   
      P1_Periph.OUT_k := (As_Array => True,
                          Arr      => (others => (if Dir = Off then Low else High)));   
   end Set_LED;
   Interval : constant Standard.Duration := 0.5;
begin
   Set_LED_Output;
   loop
      Set_LED(Off);
      -- delay Interval;
      Wait;
      Set_LED(On);
      -- delay Interval;
      Wait;
   end loop;
end Test;

I get the following warnings/errors if any of the above code is the following (which is what I want):

      P0_Periph.DIR := (As_Array => True,
                        Arr      => (LED_Pin => Output));
Note: Building test=0.1.0-dev/test.gpr...
Compile
   [Ada]          test.adb
test.adb:14:37: warning: too few elements for type "Interfaces.Nrf52.Gpio.DIR_PIN_Field_Array" [enabled by default]
test.adb:14:37: warning: expected 32 elements; found 1 element [enabled by default]
test.adb:14:37: warning: Constraint_Error will be raised at run time [enabled by default]
test.adb:42:04: warning: constant "Interval" is not referenced [-gnatwu]
Bind
   [gprbind]      test.bexch
   [Ada]          test.ali
Link
   [link]         test.adb

Why do I have to indicate others => <>?? I do not understand. Okay, I mean, the Array is packaged and maybe the compiler cannot modify a single bit? Here is the data structure from the runtime (nrf52-runtimes/nrf52_src/nrf52840/svd/i-nrf52-gpio.ads at aced423df66f7f484deb544e4495cad9e4c13784 · damaki/nrf52-runtimes · GitHub):

   --  Pin 0
   type DIR_PIN0_Field is
     (--  Pin set as input
      Input,
      --  Pin set as output
      Output)
     with Size => 1;
   for DIR_PIN0_Field use
     (Input => 0,
      Output => 1);

   --  DIR_PIN array
   type DIR_PIN_Field_Array is array (0 .. 31) of DIR_PIN0_Field
     with Component_Size => 1, Size => 32;

   --  Direction of GPIO pins
   type DIR_Register
     (As_Array : Boolean := False)
   is record
      case As_Array is
         when False =>
            --  PIN as a value
            Val : Interfaces.NRF52.UInt32;
         when True =>
            --  PIN as an array
            Arr : DIR_PIN_Field_Array;
      end case;
   end record
     with Unchecked_Union, Size => 32, Volatile_Full_Access,
          Bit_Order => System.Low_Order_First;

   for DIR_Register use record
      Val at 0 range 0 .. 31;
      Arr at 0 range 0 .. 31;
   end record;

Also, what does others => <> do exactly? Because as far as I know, it does the “default constructor/value for the elements”, which is not what I want. I want the rest of the pins to be left untouched.

The alire.toml for the project is the following:

name = "test"
description = "test"
version = "0.1.0-dev"

authors = ["tes"]
maintainers = ["tes <test@test.tes>"]
maintainers-logins = ["tes"]
licenses = "MIT OR Apache-2.0 WITH LLVM-exception"
website = ""
tags = []

executables = ["test"]

[[depends-on]]
# light_tasking_nrf52840 = "*"
embedded_nrf52840 = "*"

[configuration.values]
# light_tasking_nrf52840.LFCLK_Src = "RC"
# light_tasking_nrf52840.Time_Base = "RTC0"
embedded_nrf52840.LFCLK_Src = "RC"
embedded_nrf52840.Time_Base = "RTC0"


[gpr-set-externals]
LIGHT_TASKING_NRF52840_BUILD = "Production"
EMBEDDED_NRF52840_BUILD = "Production"

[build-switches]
"*".style_checks = "No"
"*".runtime_checks = "none"

[[actions]]
type = "post-build"
command = ["arm-eabi-size", "-A", "bin/test"]

[[actions]]
type = "post-build"
command = ["arm-eabi-objcopy", "-O","ihex", "bin/test", "bin/test.hex"]

[[actions]]
type = "post-build"
command = ["uf2conv.py", "bin/test.hex", "-f", "0xADA52840", "-o", "bin/flash.uf2"]

As you can see, I tried to use the light_tasking example (initially in debug mode, then in production). I also initally tried to compile with all assertions enabled (default in Debug), but I switched back to turning the checks off as that seems to be killing my program.

I decompiled (Iaito/Radare2 for the win!) the program and I found out that the _last_chance_handler was being called (marked as likely, even when I get no warning), so there is probably a bug somewhere. I usually fix these things in no time with gdb, however… I do not know how to properly debug the board as I think it has no OpenOCD support and no J-TAG either… Also, the board is loaded with the Adafruit_nRF52_Bootloader bootloader and I think that may be playing a role with the program… Even though I modify all GPIO to be output and to “blink”, some leds that are usually controlled by the bootloader are not responding to blinking…

Any tips here? Any tips on how to work with GDB and ARM boards (I only used OpenOCD and GDB with RISC-V).

Best regards,
Fer

1 Like

Off the top of my head, if you want to do a read-modify-write to just one field in DIR, then you need to do:

P0_Periph.DIR.Arr (LED_Pin) := Output;

That will write to just one pin, and the others will be unchanged (the compiler should do the read-modify-write of any other unmodified bits in the register).

When you’re doing this:

      P1_Periph.DIR := (As_Array => True,
                        Arr      => (others => Output));   

then you’re assigning to the entire DIR register, so you need to give a value for each element in the register (which others => Output does).

Hope that helps!

2 Likes

Thanks! You are right! I thought that in order to access the Arr component, one had to first indicate the As_Array discriminant to be true :slight_smile: It compiles and there are no warnings now! Thanks!

I now need to find out what is wrong with the code/sequence. I noticed that after your change the amount of calls to checks/last_chance_handler was reduced, and the board is now doing some different stuff, but not quite there. Oh well. I will keep on looking :slight_smile:

Best regards,
Fer