Ada-Rapporteur-Group / User-Community-Input

Ada User Community Input Working Group - Github Mirror Prototype
27 stars 1 forks source link

Improved API for Function overloading via Or Parameters. #41

Open kevlar700 opened 1 year ago

kevlar700 commented 1 year ago

First of all. This issue has only come up a couple of times for myself so far. As I have zero clue how much work will be required on the sophisticated Ada compiler then it may not be worth the effort?

I have ignored pins to show the idea succinctly. The idea is to be able to have the following instead of overloaded procedures and functions:

Any record components accessed that did not exist in both or all of the Or'd parameters would cause a compile error.

Procedure Configure_SPI (MISO_Port : in out GPIOA_Peripheral | GPIOI_Peripheral; 
                         MISO_Port : in out GPIOA_Peripheral | GPIOI_Peripheral; 
                         CLK_Port : in out GPIOA_Peripheral | GPIOI_Peripheral; 
                         CS_Port: in out GPIOA_Peripheral | GPIOI_Peripheral);

procedure Configure_GPIO (Port : in out GPIOA_Peripheral | GPIOI_Peripheral;
                          Mode : in GPIO.Mode.Operant);

Background information follows.

These files are generated from ARM CMSIS SVD xml files by adacores svd2ada tool.

"https://github.com/kevlar700/STM32L4R9_SVD/blob/main/stm32_svd-gpio.ads" "https://github.com/kevlar700/STM32L4R9_SVD/blob/main/stm32_svd-tim.ads"

I shall demonstrate the simplest case.

for GPIOA_Peripheral use record
      MODER   at 16#0# range 0 .. 31;
      OTYPER  at 16#4# range 0 .. 31;
      OSPEEDR at 16#8# range 0 .. 31;
      PUPDR   at 16#C# range 0 .. 31;
      IDR     at 16#10# range 0 .. 31;
      ODR     at 16#14# range 0 .. 31;
      BSRR    at 16#18# range 0 .. 31;
      LCKR    at 16#1C# range 0 .. 31;
      AFRL    at 16#20# range 0 .. 31;
      AFRH    at 16#24# range 0 .. 31;
      BRR     at 16#28# range 0 .. 31;
      ASCR    at 16#2C# range 0 .. 31;
   end record;

   --  General-purpose I/Os
   GPIOA_Periph : aliased GPIOA_Peripheral
     with Import, Address => GPIOA_Base;

   --  General-purpose I/Os
   GPIOB_Periph : aliased GPIOA_Peripheral
     with Import, Address => GPIOB_Base;

   --  General-purpose I/Os
   GPIOC_Periph : aliased GPIOA_Peripheral
     with Import, Address => GPIOC_Base;

   --  General-purpose I/Os
   GPIOD_Periph : aliased GPIOA_Peripheral
     with Import, Address => GPIOD_Base;

   --  General-purpose I/Os
   GPIOE_Periph : aliased GPIOA_Peripheral
     with Import, Address => GPIOE_Base;

   --  General-purpose I/Os
   GPIOF_Periph : aliased GPIOA_Peripheral
     with Import, Address => GPIOF_Base;

   --  General-purpose I/Os
   GPIOG_Periph : aliased GPIOA_Peripheral
     with Import, Address => GPIOG_Base;

   --  General-purpose I/Os
   GPIOH_Periph : aliased GPIOA_Peripheral
     with Import, Address => GPIOH_Base;

   for GPIOI_Peripheral use record
      MODER   at 16#0# range 0 .. 31;
      OTYPER  at 16#4# range 0 .. 31;
      OSPEEDR at 16#8# range 0 .. 31;
      PUPDR   at 16#C# range 0 .. 31;
      IDR     at 16#10# range 0 .. 31;
      ODR     at 16#14# range 0 .. 31;
      BSRR    at 16#18# range 0 .. 31;
      LCKR    at 16#1C# range 0 .. 31;
      AFRL    at 16#20# range 0 .. 31;
      AFRH    at 16#24# range 0 .. 31;
      BRR     at 16#28# range 0 .. 31;
   end record;

   --  General-purpose I/Os
   GPIOI_Periph : aliased GPIOI_Peripheral
     with Import, Address => GPIOI_Base;

These records differ by the one last register in GPIOA; ASCR. Leading to two distinct types being generated.

There are various ways to tackle this currently.

Create a record and use that as an all encompassing peripheral as accomplished in Ada_Drivers_Library Timer peripheral. https://github.com/AdaCore/Ada_Drivers_Library/blob/master/arch/ARM/STM32/drivers/stm32-timers.ads This has the issue of maintenance for new chips and complexity in matching to the newly generated svd.

Use function overloading. This is nice and simple but has the issue of code duplication as well as the issue of a higher level function possibly wanting to set various combinations of GPIO port types. Such as for an SPI function with MISO, MOSI, CLK, CS arguments.

I am currently considering switching from overloading to an enum of peripheral addresses as the parameter (as Integers). With a slight erosion of type safety in terms of one more layer for typos but a better API facilitation.

Mark Rogers suggested that he uses Unchecked_Unions to solve this problem.

Generics do not seem to be an option due to wanting to conduct operations on the record sub components.

Tagged type tags would cause problems for memory overlays.

p.s. I believe Ada is the best language for general embedded development. One concern I have is that the HIS portrayal of Ada may be scaring some users away. Perhaps Ada descriptions could bear that in mind?