3

I'm in the process of learning Ada using the GNAT compiler using the AdaCore GPS (GPL) IDE, aimed at ARM "bare board" hardware (STM32F4 using a Ravenscar SFP runtime).

For my ARM based embedded work I've come from a C/C++ background.

Anyway, I've implemented a "last chance handler" that's defined as follows,

procedure Last_Chance_Handler (Msg : System.Address; Line : Integer);
pragma Export (C, Last_Chance_Handler, "__gnat_last_chance_handler");

Here's the actual procedure (a snippet from the .adb file),

procedure Last_Chance_Handler (Msg : System.Address; Line : Integer) is
begin
   LEDs_All_On;
   -- Put_Line("Detail :" & Address_To_String(Msg));
   Put_Line("Line   :" & Line'Image);

   loop
      null;
   end loop;
end Last_Chance_Handler;

The Msg argument is documented as follows,

The Msg parameter is a C null-terminated string representing the source location of the raise statement, as generated by the compiler, or a zero-length string if pragma Suppress_Exception_Locations is used.

I've been trying to figure out how to convert the null terminated Msg bytes to an Ada string so that I can display it using a Put_Line() call (when debugging I can access this type of output via the semihosting mechanism).

I've previously mapped Ada records (representing device registers etc.) to physical memory addresses by setting their 'Address attribute to a constant value. However, this is the first time I've tried access memory via a System.Address value held in a variable.

Can anyone suggest how I might go about implementing the Address_To_String() procedure?

I have experimented with Ada byte arrays and unchecked conversions between them and System.Address, also with Ada pointers, but so far I've not had any success.

Any help or suggestions would be greatly appreciated!

Many thanks,

...Max

Max van Daalen
  • 317
  • 2
  • 14

3 Answers3

4

Just in case it wasn’t clear from Jacob’s answer, you’re perfectly at liberty to declare

with Interfaces.C.Strings;
procedure Last_Chance_Handler
  (Msg : Interfaces.C.Strings.chars_ptr; Line : Integer);

bearing in mind the documented description of Msg.

Simon Wright
  • 25,108
  • 2
  • 35
  • 62
3

The operation you are looking for is probably Interfaces.C.Strings.Value. It takes an argument of the type Interfaces.C.Strings.chars_ptr (equivalent to char * in C) and returns a String.

Jacob Sparre Andersen
  • 6,733
  • 17
  • 22
  • Almost got it to work but annoyingly the output is garbled, possibly an encoding issue, I'll continue to investigate. Also, the `Interfaces.C.Strings` package is only available in the "full" Ravenscar run-time, I was hoping to use "sfp". I can probably grab the code from somewhere once I've got it all working. – Max van Daalen Nov 14 '17 at 12:24
3

If you come to a last_chance_handler something is probably broken and you shouldnt rely too much on your environment. Ada.Text_IO is a very heavy package. You should try to avoid it generally and especially here.

You could try something like this:

with GNAT.IO;
with System.Storage_Elements;
procedure Last_Chance_Handler
   (Msg : System.Address; Line : Integer)
is
   use System.Storage_Elements; -- make "+" visible for System.Address

   function Peek (Addr : System.Address) return Character
   is
      C : Character with Address => Addr;
   begin
      return C;
   end Peek; 
   A : System.Address := Msg;
begin
   GNAT.IO.Put ("line :");
   GNAT.IO.Put (line); -- avoid the secondary stack for Line'Image
   GNAT.IO.New_Line;
   while Peek(A) /= ASCII.NUL loop
      GNAT.IO.Put (Peek(A));
      A := A + 1;
   end loop;
   GNAT.IO.New_Line;
end;
RREE
  • 101
  • 4
  • Nice! I had come up with a similar approach, I used `To_Integer()` to cast the `System.Address` to an `Integer_Address` and then used an `Unchecked_Conversion` to convert this to a `Character` pointer etc. However, your code is much cleaner! I also wrote my own versions of `Put()` and `Put_Line()` that use one of the on board UARTs (I'm developing on a STM32F407 bare board target). I needed these anyway, as ultimately I want to use my own 'zero footprint' run-time. I'm having a lot of fun learning Ada, many thanks for the reply! – Max van Daalen Nov 17 '17 at 09:06
  • Max is using one of AdaCore’s bare-board runtimes, in which Ada.Text_IO is very much simplified (and ends up in the same place as GNAT.IO). And, how is this better than calling Put_Line? If this approach removes Max’s problem with garbled text, something very strange is going on. – Simon Wright Nov 17 '17 at 18:08
  • I didn't know what kind of RTS Max is using, a Ravenscar RTS is a almost full RTS with only limited tasking support AFAIK (never worked with one). GNAT.IO has direct support to print integers. He uses the 'Image attribute that requires the secondary stack, that I want to avoid here. My version directly uses the C knowledge of a 0 terminated string. If you want to use Put_Line you first have to convert the zero ended string into an Ada string only to print it in a C fashion in the end. – RREE Nov 17 '17 at 19:37
  • I'm using the Adacore `ravenscar-sfp-stm32f4` (small foot print, reduced run-time) which as far as I can tell is aimed at the STM32F407G Discovery board, which I also have. Ultimately I'm planning to move to the ZFP run-time (zero foot print, without any ravenscar support). – Max van Daalen Nov 18 '17 at 00:04
  • With respect to getting garbled text, this only happened when I used the `Interfaces.C.Strings.chars_ptr` type (no idea why). When I finally figured out how to use the `System.Address` directly it worked, I later tidied up the code based on the snippet above. I was also using semihosting via the ST-Link interface, this was far from ideal and would occasionally cause problems. – Max van Daalen Nov 18 '17 at 00:04
  • As I think I mentioned, I've now written my own USART based IO package, it's basic but is working well (via the ST-Link VCP ). connection, I had to add a couple of bodge wires to the discovery board to connect USART2 to the ST-Link's M0, it's not connected by default and there are no ready made jumpers to do this - shame). – Max van Daalen Nov 18 '17 at 00:08
  • Earlier, I mean to say that it was `Interfaces.C.Strings.Value` that returned garbled strings. I didn't really investigate it fully as I discovered that this operation isn't included with the zero footprint run-time, hence using `System.Address` directly. – Max van Daalen Nov 18 '17 at 10:43