Home >> Features >> Event-Driven Processor Programming

Event-Driven Processor Programming

advertisement:

Almost since computers were invented, interrupts have been a common programming method to deal with real-time tasks. An interrupt causes a processor to stop the running task, and to execute an interrupt handler instead. The interrupt handler determines the cause of the interrupt, responds to the interrupt, whereupon control is restored to the original task. A simple example is an interrupt from a UART (a serial port) stating that a character has been received, and the interrupt handler will take the character from the UART and store it in a queue in memory for use by the main task.

Figure 1. Example task structure. The background task is green, the interrupt handlers are red, and arrows show interaction.
Figure 1. Example task structure. The background task is green, the interrupt handlers are red, and arrows show interaction.
Programming with interrupts can be complex and challenging, so in this article we outline a programming method called event-driven programming that offers most of the functionality of interrupts, while being easier to reason about.

Interrupts

To show why interrupts are a challenge, consider an Ethernet time-server. This is a device connected to a high precision clock that supplies a global reference clock using an Ethernet protocol. At the core, this device has four tasks, serviced by three interrupts (Figure 1):

• The background task responds to a time request that comes in over Ethernet. It will, for example, provide and implement a protocol that estimates the latency between Ethernet transmitter and receiver.
• On a clock interrupt: when a new clock reading is available, the clock value is incremented by one. Assuming we have a 64-bit clock value, this may involve multiple operations to implement the long addition.
• On an EthernetTX interrupt: check if there is another packet to be sent, and if so, provide it.
• On an EthernetRX interrupt: queue and timestamp the packet.

We can see here that interrupts serve two fundamental purposes to the system programmer: they offer a method to react to external stimuli (Ethernet, clock), and a method to implement semi-concurrent execution of code (between a background task and the other three tasks).

Programming this system is complex. Consider the clock interrupt, which performs an operation along the following lines (assuming a 32-bit architecture where LSW is the least significant word and MSW is the most significant word):

clock.LSW = clock.LSW + 1;
if (clock.LSW == 0)
   clock.MSW = clock.MSW + 1;

The background task may observe a time-stamp at any time by reading the value from clock.LSW and clock.MSW. There are two considerations when implementing this:

• If the clock interrupt happens just between the background task reading the LSW and the MSW, the background task may get an inconsistent view of the time (off by 4 billion ticks). The chances of this happening are slim (1 in 4 billion), which means that this problem is very unlikely to show up during testing. This problem can be solved by protecting the read operation, for example, by disabling around the read.
• Since the interrupt may happen at any time, the interrupt routine can make little or no assumptions about the state of the registers. It therefore must save all registers that are used in the interrupt routine, and restore all registers afterwards. In addition, it must load any values it needs.



Trending this Month

Newsletter

Subscribe today to receive the INSIDER, a FREE e-mail newsletter from Embedded Technology featuring exclusive previews of upcoming articles, late breaking NASA and industry news, hot products and design ideas, links to online resources, and much more.

Sign up now >>