I’m trying to migrate an Ada application that communicates with a piece of hardware over a serial port, from Linux poll() and read() to GNAT.Serial_Communications.
I’m using a GCC 14.2.0 cross-toolchain targeting AArch64 and running my software on a Raspberry Pi 3.
I have found what appears to be a bug, documentation deficiency and/or an annoyance: The Timeout value (of type Duration) seems to be rounded to the nearest 100 milliseconds. The following reduced test program illustrates:
WITH Ada.IO_Exceptions;
WITH Ada.Real_Time;
WITH Ada.Text_IO; USE Ada.Text_IO;
WITH GNAT.Serial_Communications;
USE TYPE Ada.Real_Time.Time;
PROCEDURE test_sercom IS
PACKAGE sercom RENAMES GNAT.Serial_Communications;
port : ALIASED sercom.Serial_Port;
p : CONSTANT ACCESS sercom.Serial_Port := port'Access;
c : Character;
t0 : Ada.Real_Time.Time;
t1 : Ada.Real_time.Time;
BEGIN
sercom.Open(port, "/dev/ttyAMA0");
sercom.Set(port, sercom.B115200, Block => True, Timeout => 0.051);
FOR i IN 1 .. 10 LOOP
t0 := Ada.Real_Time.Clock;
BEGIN
Character'Read(p, c);
Put(c);
EXCEPTION
WHEN Ada.IO_Exceptions.End_Error | GNAT.Serial_Communications.Serial_Error =>
NULL;
END;
t1 := Ada.Real_Time.Clock;
Put_Line(Duration'Image(Ada.Real_Time.To_Duration(t1 - t0)));
END LOOP;
END test_sercom;
With a Timeout value of 0.51, I get the following output (not exactly what I expected, but close enough):
0.102885328
0.102148562
0.103959852
0.103983029
0.103982769
0.103981623
0.103985842
0.103979852
0.103984279
0.103985689
With a Timeout value of 0.49, I get the following erroneous output:
0.001932892
0.000055260
0.000049583
0.000049010
0.000049895
0.000049635
0.000049062
0.000048958
0.000049479
0.000048437
I would like to achieve a loop cycle time of about 1 to 5 ms, which I can tune for responsiveness versus CPU utilization. (My actual code has the loop in a background task that owns the serial port. The main program (environment task) communicates with the background task via a pair of FIFO queues, one for each direction, as is commonly done with FreeRTOS on single chip microcontrollers).
Unfortunately the GNAT.Serial_Communications in GCC 14.2.0 seems to allow loop cycle times of either 48 to 55 microseconds or 103 milliseconds.
I’ve also noticed another anomaly: In my code the Block parameter to GNAT.Serial_Communications.Set has no effect despite what the internal documentation asserts.
For now I’ve implemented a workaround: Replace the NULL statement in the exception handler with DELAY 0.005. I’ve sort of convinced myself that this will be mostly equivalent to the timeout working properly and testing seems to affirm this.