comp.lang.ada
 help / color / mirror / Atom feed
* Protected Objects and Interrupt Handlers
@ 2016-02-23 22:25 Jere
  2016-02-23 23:09 ` Simon Wright
  2016-02-25 15:14 ` Maciej Sobczak
  0 siblings, 2 replies; 14+ messages in thread
From: Jere @ 2016-02-23 22:25 UTC (permalink / raw)


This is more of a curiosity, but I have noticed that a lot of embedded Ada examples that I run across use a protected type object to wrap the ISR for a particular interrupt.  I was wondering why this is?  Why not just use a normal Ada procedure for an interrupt?  I can understand using a protected object to handle procedures used by different tasks, but an ISR isn't a task per say.  So how do Ada's protected type object and procedure implementations work with interrupts?


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Protected Objects and Interrupt Handlers
  2016-02-23 22:25 Protected Objects and Interrupt Handlers Jere
@ 2016-02-23 23:09 ` Simon Wright
  2016-02-24 14:38   ` Jere
  2016-02-25 15:14 ` Maciej Sobczak
  1 sibling, 1 reply; 14+ messages in thread
From: Simon Wright @ 2016-02-23 23:09 UTC (permalink / raw)


Jere <jere.groups@gmail.com> writes:

> This is more of a curiosity, but I have noticed that a lot of embedded
> Ada examples that I run across use a protected type object to wrap the
> ISR for a particular interrupt.  I was wondering why this is?  Why not
> just use a normal Ada procedure for an interrupt?  I can understand
> using a protected object to handle procedures used by different tasks,
> but an ISR isn't a task per say.  So how do Ada's protected type
                          per se (Latin for "by itself" or "in itself")
> object and procedure implementations work with interrupts?

You could certainly use a procedure as an ISR, if you knew how to do
it. You can see this being done in [1]; note needing to export the
procedure, and set up its address in the interrupt vector table, also
fun with the NVIC.

But - in general - what is the ISR going to do in order to get the
interrupt and associated data into the rest of the program? You'd hardly
want the main program busy-waiting while polling to see whether
interrupts have happened; might as well not use interrupts at all.

A task is a thread of execution, and so is an interrupt, so it seems
natural to use the same mechanism to do interrupt-to-task communication
as for task-to-task; the originating task/interrupt calls a procedure in
the PO which results in waking up the listening task.

You may have traced the ARM stuff on interrupt handling (C.3ff, [2]).

I wrote up how GNAT/Ravenscar deals with this at [3] (section "Interrupt
handling", about 2/3 of the way down).

[1] http://www.inspirel.com/articles/Ada_On_Cortex_Interrupts.html
[2] http://www.ada-auth.org/standards/12rm/html/RM-C-3.html
[3] http://forward-in-code.blogspot.co.uk/2015/06/building-runtime-system-for-arm-eabi.html

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Protected Objects and Interrupt Handlers
  2016-02-23 23:09 ` Simon Wright
@ 2016-02-24 14:38   ` Jere
  2016-02-24 18:29     ` Simon Wright
  0 siblings, 1 reply; 14+ messages in thread
From: Jere @ 2016-02-24 14:38 UTC (permalink / raw)


On Tuesday, February 23, 2016 at 6:09:30 PM UTC-5, Simon Wright wrote:
> A task is a thread of execution, and so is an interrupt, so it seems
> natural to use the same mechanism to do interrupt-to-task communication
> as for task-to-task; the originating task/interrupt calls a procedure in
> the PO which results in waking up the listening task.
> 
> You may have traced the ARM stuff on interrupt handling (C.3ff, [2]).
> 
> I wrote up how GNAT/Ravenscar deals with this at [3] (section "Interrupt
> handling", about 2/3 of the way down).
> 
> [1] http://www.inspirel.com/articles/Ada_On_Cortex_Interrupts.html
> [2] http://www.ada-auth.org/standards/12rm/html/RM-C-3.html
> [3] http://forward-in-code.blogspot.co.uk/2015/06/building-runtime-system-for-arm-eabi.html

Thanks for those links and the discussion.  Keep in mind I come from a small micro world, so I am used to handling interrupts directly, which is part of my confusion.  Interrupts happen at the lowest level and tend to interrupt the main processing (which would include all the tasks/threads/processes/whathaveya).  I can understand from a general view point they are all threads of execution, but interrupt service routines as I have typically been able to use them operate very differently than a task or thread or process.  ISR's are jumped to directly by the micro and are manually serviced with the hardware handling context switches (as I am used to at least).  While as I understand it, tasks are a higher level concept that happens in main processing using some sort of Runtime construct but with additional overhead such as data structures to hold the task state for context switches.

I would expect a protected entry or procedure to employ a different type of synchronization construct (maybe similar to a mutex/semiphore/etc.??) to keep multiple tasks from changing the data in the protected object at the same time than is needed for interrupts.  For interrupts I would expect something more like disabling interrupts to be appropriate.  Does a protected object/procedure behave differently for interrupts than it does for tasks under the hood and Ada is able to handle all those details based on the existence of the pragma used?

I'll read through the links you provided to get more info as well.  Thank you for those!

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Protected Objects and Interrupt Handlers
  2016-02-24 14:38   ` Jere
@ 2016-02-24 18:29     ` Simon Wright
  2016-02-24 21:37       ` Jere
  0 siblings, 1 reply; 14+ messages in thread
From: Simon Wright @ 2016-02-24 18:29 UTC (permalink / raw)


Jere <jere.groups@gmail.com> writes:

> I would expect a protected entry or procedure to employ a different
> type of synchronization construct (maybe similar to a
> mutex/semiphore/etc.??) to keep multiple tasks from changing the data
> in the protected object at the same time than is needed for
> interrupts.  For interrupts I would expect something more like
> disabling interrupts to be appropriate. Does a protected
> object/procedure behave differently for interrupts than it does for
> tasks under the hood and Ada is able to handle all those details based
> on the existence of the pragma used?

GNAT (don't forget there are other Ada compilers!) handles differences
between tasks & interrupts calling protected procedures very similarly;
the difference is, I believe, in the way locks are handled.

I don't know too much about the way AdaCore's bare board implementation
does this; the way I dealt with this at [1] was to implement
System.Tasking.Protected_Objects.Lock and .Lock_Read_Only as

   if in an ISR then
      null;
   elsif this PO's ceiling priority is an interrupt priority then
      --  we are in a task, but this PO deals with interrupts, so we
      --  mustn't be interrupted
      disable interrupts;
   else
      --  we are in a task and this PO doesn't deal with interrupts
      take the PO's mutex;
      raise the priority to the PO's ceiling
   end if;

[1] https://sourceforge.net/projects/cortex-gnat-rts/


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Protected Objects and Interrupt Handlers
  2016-02-24 18:29     ` Simon Wright
@ 2016-02-24 21:37       ` Jere
  0 siblings, 0 replies; 14+ messages in thread
From: Jere @ 2016-02-24 21:37 UTC (permalink / raw)


On Wednesday, February 24, 2016 at 1:29:24 PM UTC-5, Simon Wright wrote:
> GNAT (don't forget there are other Ada compilers!) handles differences
> between tasks & interrupts calling protected procedures very similarly;
> the difference is, I believe, in the way locks are handled.
> 
> I don't know too much about the way AdaCore's bare board implementation
> does this; the way I dealt with this at [1] was to implement
> System.Tasking.Protected_Objects.Lock and .Lock_Read_Only as
> 
>    if in an ISR then
>       null;
>    elsif this PO's ceiling priority is an interrupt priority then
>       --  we are in a task, but this PO deals with interrupts, so we
>       --  mustn't be interrupted
>       disable interrupts;
>    else
>       --  we are in a task and this PO doesn't deal with interrupts
>       take the PO's mutex;
>       raise the priority to the PO's ceiling
>    end if;
> 
> [1] https://sourceforge.net/projects/cortex-gnat-rts/

Ok, that makes more sense then.  Thank you!


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Protected Objects and Interrupt Handlers
  2016-02-23 22:25 Protected Objects and Interrupt Handlers Jere
  2016-02-23 23:09 ` Simon Wright
@ 2016-02-25 15:14 ` Maciej Sobczak
  2016-02-25 15:59   ` Simon Wright
  2016-02-25 16:02   ` Simon Wright
  1 sibling, 2 replies; 14+ messages in thread
From: Maciej Sobczak @ 2016-02-25 15:14 UTC (permalink / raw)



> This is more of a curiosity, but I have noticed that a lot of embedded Ada examples that I run across use a protected type object to wrap the ISR for a particular interrupt.

The way I understand it is that in a more complex system interrupt routines are supposed to be as short as possible and only do the necessary work of *notifying* the regular tasks that something of interest have happened, possibly translating it into some higher-level concept like message arrival or button being pressed, etc. The regular tasks are then responsible for actual processing of this information.
In such a scheme, protected objects readily provide the necessary tasking pattern - the regular tasks *wait* for some work to do, perhaps in terms of calling appropriate *entry* of the protected object. The interrupt (which is a low-level concept) is physically delivered to the *procedure* in the protected object, where some predicate is set so that the entry-waiting task is let in and allowed to proceed.

This is no different from a regular work-queue pattern, where "work item" is delivered to some protected procedure and a worker task gets that item from the entry, possibly blocking if at the given moment there is no item to process. The idea is to retain this high-level multitasking pattern with interrupts, which I find pretty nice.

Note also that by tying interrupts with protected objects, you retain the possibility to reason about different properties of the code (like freedom from deadlocks) in the same way as in a regular multitasking program. This is an added value.

Last but no least - Simon has pointed you towards an article (a book chapter, in fact) where the interrupts are handled by regular procedures instead - as an author of that text I feel responsible for clarifying why - the reason is that the book consistently assumes a zero-runtime execution platform, where the necessary scaffolding for protected objects (namely: actual synchronization) simply does not exist.
Note also that it is easy to combine these two approaches - just let the interrupt handler (a procedure) interact with a dedicated protected object (by forwarding to its procedures) as if it was a regular work-item producing task - which, conceptually, it really is.

-- 
Maciej Sobczak * http://www.inspirel.com


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Protected Objects and Interrupt Handlers
  2016-02-25 15:14 ` Maciej Sobczak
@ 2016-02-25 15:59   ` Simon Wright
  2016-02-26  3:20     ` Dennis Lee Bieber
  2016-02-27 18:06     ` Maciej Sobczak
  2016-02-25 16:02   ` Simon Wright
  1 sibling, 2 replies; 14+ messages in thread
From: Simon Wright @ 2016-02-25 15:59 UTC (permalink / raw)


Maciej Sobczak <see.my.homepage@gmail.com> writes:

> Note also that it is easy to combine these two approaches - just let
> the interrupt handler (a procedure) interact with a dedicated
> protected object (by forwarding to its procedures) as if it was a
> regular work-item producing task - which, conceptually, it really is.

I think this approach might have problems; if the PO doesn't expect to
be called in an interrupt context, it may do things (like block) which
an ISR should never do.

The AdaCore sfp RTS (the 2014 version, anyway) says (comments removed)

   procedure Lock (Object : Protection_Access) is
      Self_Id         : constant Task_Id := Self;
      Caller_Priority : constant Any_Priority := Get_Priority (Self_Id);
   begin
      if Object.Owner = Self_Id then
         raise Program_Error;
      end if;
      if Caller_Priority > Object.Ceiling then
         raise Program_Error;
      end if;
      Set_Priority (Self_Id, Object.Ceiling);
      if Multiprocessor then
         Multiprocessors.Fair_Locks.Lock (Object.Lock);
      end if;
      Object.Owner := Self_Id;
      Object.Caller_Priority := Caller_Priority;
      Self_Id.Common.Protected_Action_Nesting :=
        Self_Id.Common.Protected_Action_Nesting + 1;
   end Lock;

where the *_Id references are to the currently running task, which of
course has no relationship to the current ISR.

(I have to say that, given that Lock is called in interrupt contexts (I
just checked with the debugger) I don't see how this works at all!
Perhaps GNAT detects AdaCore's RTS and generates different code? Also,
the 2015 version of Lock is the same)

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Protected Objects and Interrupt Handlers
  2016-02-25 15:14 ` Maciej Sobczak
  2016-02-25 15:59   ` Simon Wright
@ 2016-02-25 16:02   ` Simon Wright
  2016-02-25 17:40     ` Tero Koskinen
  1 sibling, 1 reply; 14+ messages in thread
From: Simon Wright @ 2016-02-25 16:02 UTC (permalink / raw)


Maciej Sobczak <see.my.homepage@gmail.com> writes:

> Last but no least - Simon has pointed you towards an article (a book
> chapter, in fact) where the interrupts are handled by regular
> procedures instead - as an author of that text I feel responsible for
> clarifying why - the reason is that the book consistently assumes a
> zero-runtime execution platform, where the necessary scaffolding for
> protected objects (namely: actual synchronization) simply does not
> exist.

A minor point: a common cited advantage of the Cortex series is that
ISRs can be written in ordinary code, without compiler magic. This seems
to imply that other MCUs _do_ need compiler magic!


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Protected Objects and Interrupt Handlers
  2016-02-25 16:02   ` Simon Wright
@ 2016-02-25 17:40     ` Tero Koskinen
  2016-02-25 19:49       ` Simon Wright
  0 siblings, 1 reply; 14+ messages in thread
From: Tero Koskinen @ 2016-02-25 17:40 UTC (permalink / raw)


Hi,

25.2.2016, 18.02, Simon Wright wrote:
> Maciej Sobczak <see.my.homepage@gmail.com> writes:
>
>> Last but no least - Simon has pointed you towards an article (a
>> book chapter, in fact) where the interrupts are handled by regular
>> procedures instead - as an author of that text I feel responsible
>> for clarifying why - the reason is that the book consistently
>> assumes a zero-runtime execution platform, where the necessary
>> scaffolding for protected objects (namely: actual synchronization)
>> simply does not exist.
>
> A minor point: a common cited advantage of the Cortex series is that
> ISRs can be written in ordinary code, without compiler magic. This
> seems to imply that other MCUs _do_ need compiler magic!

Yes, ARM Cortex-Mx family processors can have normal C functions as
interrupt handlers because when the interrupt occurs the hardware pushes
a set of registers to stack automatically (and also restores
the registers when returning from the interrupt).

On the other hand, 8-bit AVRs do not have this feature, so for them you
need to mark the interrupt handler somehow so that the compiler can
generate different code for them.

In general, ARM Cortex-Mx processors are "C-friendly". For example
in the normal case, you can have all the startup code in C (or
in Ada, which is then exported to C) and there is no need for
assembler.

Yours,
  Tero


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Protected Objects and Interrupt Handlers
  2016-02-25 17:40     ` Tero Koskinen
@ 2016-02-25 19:49       ` Simon Wright
  2016-03-13  8:10         ` Simon Wright
  0 siblings, 1 reply; 14+ messages in thread
From: Simon Wright @ 2016-02-25 19:49 UTC (permalink / raw)


Tero Koskinen <tero.koskinen@iki.fi> writes:

> For example in the normal case, you can have all the startup code in C
> (or in Ada, which is then exported to C)

Actually, very little even needs exporting to C! I had one that I've
given convention Asm (it's invoked by the linker script), but I'm not at
all sure it needs it.

Mind you,

   procedure Program_Initialization
   with
     Export,
     Convention => Asm,
     External_Name => "program_initialization",
     No_Return;
   pragma Machine_Attribute (Program_Initialization, "naked");

   procedure Program_Initialization is
   begin
      --  _estack: the first address after the top of stack space
      System.Machine_Code.Asm ("ldr sp, =_estack", Volatile => True);
      Complete_Program_Initialization;
   end Program_Initialization;

probably isn't going to cause much trouble anyway.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Protected Objects and Interrupt Handlers
  2016-02-25 15:59   ` Simon Wright
@ 2016-02-26  3:20     ` Dennis Lee Bieber
  2016-02-26  8:12       ` Simon Wright
  2016-02-27 18:06     ` Maciej Sobczak
  1 sibling, 1 reply; 14+ messages in thread
From: Dennis Lee Bieber @ 2016-02-26  3:20 UTC (permalink / raw)


On Thu, 25 Feb 2016 15:59:46 +0000, Simon Wright <simon@pushface.org>
declaimed the following:

>Maciej Sobczak <see.my.homepage@gmail.com> writes:
>
>> Note also that it is easy to combine these two approaches - just let
>> the interrupt handler (a procedure) interact with a dedicated
>> protected object (by forwarding to its procedures) as if it was a
>> regular work-item producing task - which, conceptually, it really is.
>
>I think this approach might have problems; if the PO doesn't expect to
>be called in an interrupt context, it may do things (like block) which
>an ISR should never do.
>
	As I recall, Ada protected objects are not supposed to contain anything
that could block.

http://www.iuma.ulpgc.es/users/jmiranda/gnat-rts/node25.htm#SECTION00814000000000000000
-- 
	Wulfraed                 Dennis Lee Bieber         AF6VN
    wlfraed@ix.netcom.com    HTTP://wlfraed.home.netcom.com/

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Protected Objects and Interrupt Handlers
  2016-02-26  3:20     ` Dennis Lee Bieber
@ 2016-02-26  8:12       ` Simon Wright
  0 siblings, 0 replies; 14+ messages in thread
From: Simon Wright @ 2016-02-26  8:12 UTC (permalink / raw)


Dennis Lee Bieber <wlfraed@ix.netcom.com> writes:

> On Thu, 25 Feb 2016 15:59:46 +0000, Simon Wright <simon@pushface.org>
> declaimed the following:
>
>>Maciej Sobczak <see.my.homepage@gmail.com> writes:
>>
>>> Note also that it is easy to combine these two approaches - just let
>>> the interrupt handler (a procedure) interact with a dedicated
>>> protected object (by forwarding to its procedures) as if it was a
>>> regular work-item producing task - which, conceptually, it really is.
>>
>>I think this approach might have problems; if the PO doesn't expect to
>>be called in an interrupt context, it may do things (like block) which
>>an ISR should never do.
>>
> 	As I recall, Ada protected objects are not supposed to contain anything
> that could block.
>
> http://www.iuma.ulpgc.es/users/jmiranda/gnat-rts/node25.htm#SECTION00814000000000000000

That's for code _inside_ the PO. As the document you reference says,

   In general, code executed inside a protected object should be as
   brief as possible. This is because whilst the code is being executed
   other tasks are delayed when they try to gain access to the protected
   object.

and you wouldn't want an ISR to be "delayed when [it tries] to gain
access to the protected object".

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Protected Objects and Interrupt Handlers
  2016-02-25 15:59   ` Simon Wright
  2016-02-26  3:20     ` Dennis Lee Bieber
@ 2016-02-27 18:06     ` Maciej Sobczak
  1 sibling, 0 replies; 14+ messages in thread
From: Maciej Sobczak @ 2016-02-27 18:06 UTC (permalink / raw)



> I think this approach might have problems; if the PO doesn't expect to
> be called in an interrupt context, it may do things (like block) which
> an ISR should never do.

Good point, but this problem is not easy to avoid. Note that protected procedures cannot be called when any other task is inside the entry, so there will be some periods of time when calling the procedure will be prohibited no matter whether it is called from the handler or is itself a handler. How this is done is another story, but disabling interrupts altogether seems to be a very heavy-handed option. Note also that if the interrupt handler has something to do, moving as much of this work outside of the protected object (that is, *before* calling the protected procedure) reduces the amount of time the handler is inside the critical section, so I would still defend the idea of having the non-protected handler doing the data preparation and then calling the protected procedure just for the data exchange.

Interestingly, the C standard does not have any reasonable solution for this, except the explicit provision for communication via some dedicated atomic primitive type. I mention this not to criticize C, but to point out that in practice and depending on circumstances (ie. on the actual target), this might be the only valid solution in Ada, too.

-- 
Maciej Sobczak * http://www.inspirel.com

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Protected Objects and Interrupt Handlers
  2016-02-25 19:49       ` Simon Wright
@ 2016-03-13  8:10         ` Simon Wright
  0 siblings, 0 replies; 14+ messages in thread
From: Simon Wright @ 2016-03-13  8:10 UTC (permalink / raw)


Simon Wright <simon@pushface.org> writes:

> Tero Koskinen <tero.koskinen@iki.fi> writes:
>
>> For example in the normal case, you can have all the startup code in C
>> (or in Ada, which is then exported to C)
>
> Actually, very little even needs exporting to C! I had one that I've
> given convention Asm (it's invoked by the linker script), but I'm not at
> all sure it needs it.
>
> Mind you,
>
>    procedure Program_Initialization
>    with
>      Export,
>      Convention => Asm,
>      External_Name => "program_initialization",
>      No_Return;
>    pragma Machine_Attribute (Program_Initialization, "naked");
>
>    procedure Program_Initialization is
>    begin
>       --  _estack: the first address after the top of stack space
>       System.Machine_Code.Asm ("ldr sp, =_estack", Volatile => True);
>       Complete_Program_Initialization;
>    end Program_Initialization;
>
> probably isn't going to cause much trouble anyway.

The thinking behind that little bit of asm was that the SAM3X8E has a
bootstrap loader in (a separate section of) FLASH, and a reset mode that
uses it, and you can mistakenly leave the MCU in bootstrap mode, and thi
should recover it .. but I'm now pretty sure it desn't.

Corrent work, on STM32F429I, manages an Ada startup with two assembler
instructions (dsb, isb to flush the caches after enabling the FPU).

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2016-03-13  8:10 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-02-23 22:25 Protected Objects and Interrupt Handlers Jere
2016-02-23 23:09 ` Simon Wright
2016-02-24 14:38   ` Jere
2016-02-24 18:29     ` Simon Wright
2016-02-24 21:37       ` Jere
2016-02-25 15:14 ` Maciej Sobczak
2016-02-25 15:59   ` Simon Wright
2016-02-26  3:20     ` Dennis Lee Bieber
2016-02-26  8:12       ` Simon Wright
2016-02-27 18:06     ` Maciej Sobczak
2016-02-25 16:02   ` Simon Wright
2016-02-25 17:40     ` Tero Koskinen
2016-02-25 19:49       ` Simon Wright
2016-03-13  8:10         ` Simon Wright

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox