Home > Interrupts, Projects, RTOS, SKC++ > SKC++: Interrupt handling

SKC++: Interrupt handling

Tuesday, December 15, 2009 Leave a comment Go to comments

When it comes to interrupts, SKC++’s guiding principle is “minimum interference”.

Some kernels require that interrupts be processed internally, the internal Interrupt Service Routine (ISR) calling out to an ordinary user function, sometimes also called an ISR, confusingly. Some others let you code the ISR yourself but impose restrictions on how you do it.

SKC++ does not require you to alter the way you program ISRs; you write them just the way you would if there were no real-time kernel in the system. However, an interrupt in a multi-tasking system does not usually act alone; some communication with the rest of the system is normally required.

Communication, not-so-mutual exclusion and interrupt latency

An ISR commonly has to signal its occurrence to some task in the system. It may also pass data to or receive data from such a task. There are two basic approaches:

  • allow ISRs to make a subset of normal system calls, or
  • have special system calls “tuned” for interrupt processing.
Using normal system calls

At first glance, it seems very convenient if an ISR can post an event or send a message in the normal way. Of course, an ISR may not make a blocking call; it cannot, for example, wait for a message which might not yet be there. Indeed, in SKC++ and other kernels which implement direct message-passing, an ISR cannot wait for an event or message at all, for it is not a task to which an event could be posted or a message sent, but merely a function which borrows some stack for its execution from whichever task happens to be running at the time the interrupt occurs.

A message, therefore, can be sent from an ISR to a task but not vice versa. So message-passing cannot always be used as desired. By way of example, consider the reception and transmission of data via a USART. The USART is normally set up to interrupt when it has received a byte or when it is ready to transmit one. In the case of reception, it would be typical to let the ISR buffer the individual bytes, passing the buffer on to the related task when it is full or on receipt of some special terminating character. This could be done by sending a message. In the case of transmission, the task cannot pass a similar buffer to the ISR by this method. Either the ISR must signal every transmit interrupt to the task, which must then itself send every individual byte to the USART, or we must adopt some kind of shared-memory or buffer-ring approach.

So the usefulness of message-passing is limited. However, posting an event is a fundamentally easy way of signalling, from ISR to task, that an interrupt has occurred, even though this doesn’t solve the data transfer problem.

It turns out, though, that using SKC++’s normal post/wait pair of functions for ISR-to-task signalling has another drawback. Whatever kind of signalling we use, the kernel must access some shared memory in dealing with it. For example, a post might cause the receiving task to become ready (having been waiting, previously). The task then has to be placed on the kernel’s ready-list. This involves some pointer manipulation which has to be protected from pre-emption. This protection must be applied within the heart of the post function and also within the heart of the wait function. If ISRs can call post, protection can be provided only by disabling interrupts during the critical operations. This disabling may be explicit or it may, on some platforms, be achieved implicitly, using special “atomic” operations at assembly level, but it must be done. There is nothing “mutual” about the exclusion methods which have to be used when dealing with interrupts, as an ISR, being unable to make a blocking call, cannot participate in any kind of mutex protocol.

Disabling interrupts for brief periods within the kernel increases interrupt latency which, for obvious reasons, should be kept as low as possible. There are two parameters to consider: the maximum latency and the mean latency. The maximum latency is simply the maximum time for which interrupts can ever be disabled within the system. This figure can be kept low by careful design but for a given platform there is some limit to what can be achieved. The mean latency, though, depends also on the frequency with which interrupts are disabled. There may be many post and wait calls occurring which have nothing to do with interrupts yet each call, in the scheme being outlined, will briefly disable interrupts. This results in an unnecessarily high mean latency. We can do better…

Specialised interrupt processing

If ISRs are not permitted to make normal system calls, interrupts need not be disabled in the processing of those calls; it is sufficient to defer task scheduling, instead, to avoid pre-emption (by other tasks) within the critical regions. However, we then need some other specialised system call to signal the occurrence of an interrupt and a corresponding call for the destination task to use to detect it. This pair, of course, may still have to disable interrupts at times but by optimising the functions for their specialised purpose we can trim the maximum interrupt latency somewhat. And the mean interrupt latency falls dramatically because these calls are used only for interrupt processing and not generally, like post and wait, which now do not disable interrupts at all. Furthermore, it is possible, with specialised calls, to restrict interrupt disabling to the particular interrupt concerned, in each case.

Decision

When I first started this project, I intended to allow ISRs to post events and send messages, using post and send. I have since convinced myself that this is the wrong approach. So I have added postInterrupt and waitInterrupt to SKC++’s API. The ISR uses postInterrupt to post a simple numeric value (of type unsigned int) to an associated task. The task picks up this value from waitInterrupt, which it calls instead of the normal wait function. Note that the task associated with an interrupt would not normally use wait at all; the principle of waiting at only one point for one thing (using waitInterrupt, in this case) should be upheld. Any more elaborate processing should be delegated to a further task.

Data transfer

It is often the case that an interrupt needs to transfer more than a single unsigned int, by way of data, to its associated task. Likewise, a means is often needed to allow an ISR to access data provided by the task. These needs can be met by using a ring of two or more buffers. Each buffer belongs, at any time, either to the task or to the ISR. If ownership is indicated by simple flags which can be atomically set and reset, it is possible to use such a ring without ever needing to disable interrupts. Some care is needed to ensure that the buffer ring can never overflow but this degree of care can reasonably be expected at the System Design level where interrupts are dealt with.

To assist with this, SKC++ provides the template class BufferRing, which can be used to set up a ring of objects of any desired class.

Advertisements
Categories: Interrupts, Projects, RTOS, SKC++ Tags:
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: