What communicates with peripheral devices?

A device requires both input and output to be useful. There are a number of common concepts required for useful communication with peripherals.

An interrupt allows the device to literally interrupt the processor to flag some information. For example, when a key is pressed, an interrupt is generated to deliver the key-press event to the operating system. Each device is assigned an interrupt by some combination of the operating system and BIOS.

Devices are generally connected to an programmable interrupt controller [PIC], a separate chip that is part of the motherboard which buffers and communicates interrupt information to the main processor. Each device has a physical interrupt line between it an one of the PIC's provided by the system. When the device wants to interrupt, it will modify the voltage on this line.

A very broad description of the PIC's role is that it receives this interrupt and converts it to a message for consumption by the main processor. While the exact procedure varies by architecture, the general principle is that the operating system has configured an interrupt descriptor table which pairs each of the possible interrupts with a code address to jump to when the interrupt is received. This is illustrated in Figure 3.1.1.1, Overview of handling an interrupt.

Writing this interrupt handler is the job of the device driver author in conjunction with the operating system.

Figure 3.1.1.1 Overview of handling an interrupt

Most drivers will split up handling of interrupts into bottom and top halves. The bottom half will acknowledge the interrupt, queue actions for processing and return the processor to what it was doing quickly. The top half will then run later when the CPU is free and do the more intensive processing. This is to stop an interrupt hogging the entire CPU.

3.1.1.1 Saving state

Since an interrupt can happen at any time, it is important that you can return to the running operation when finished handling the interrupt. It is generally the job of the operating system to ensure that upon entry to the interrupt handler, it saves any state; i.e. registers, and restores them when returning from the interrupt handler. In this way, apart from some lost time, the interrupt is completely transparent to whatever happens to be running at the time.

3.1.1.2 Interrupts v traps and exceptions

While an interrupt is generally associated with an external event from a physical device, the same mechanism is useful for handling internal system operations. For example, if the processor detects conditions such as an access to invalid memory, an attempt to divide-by-zero or an invalid instruction, it can internally raise an exception to be handled by the operating system. It is also the mechanism used to trap into the operating system for system calls, as discussed in Section 3, System Calls and to implement virtual memory, as discussed in Chapter 6, Virtual Memory. Although generated internally rather than from an external source, the principles of asynchronously interrupting the running code remains the same.

3.1.1.3 Types of interrupts

There are two main ways of signalling interrupts on a line — level and edge triggered.

Level-triggered interrupts define voltage of the interrupt line being held high to indicate an interrupt is pending. Edge-triggered interrupts detect transitions on the bus; that is when the line voltage goes from low to high. With an edge-triggered interrupt, a square-wave pulse is detected by the PIC as signalling and interrupt has been raised.

The difference is pronounced when devices share an interrupt line. In a level-triggered system, the interrupt line will be high until all devices that have raised an interrupt have been processed and un-asserted their interrupt.

In an edge-triggered system, a pulse on the line will indicate to the PIC that an interrupt has occurred, which it will signal to the operating system for handling. However, if further pulses come in on the already asserted line from another device.

The issue with level-triggered interrupts is that it may require some considerable amount of time to handle an interrupt for a device. During this time, the interrupt line remains high and it is not possible to determine if any other device has raised an interrupt on the line. This means there can be considerable unpredictable latency in servicing interrupts.

With edge-triggered interrupts, a long-running interrupt can be noticed and queued, but other devices sharing the line can still transition [and hence raise interrupts] while this happens. However, this introduces new problems; if two devices interrupt at the same time it may be possible to miss one of the interrupts, or environmental or other interference may create a spurious interrupt which should be ignored.

3.1.1.4 Non-maskable interrupts

It is important for the system to be able to mask or prevent interrupts at certain times. Generally, it is possible to put interrupts on hold, but a particular class of interrupts, called non-maskable interrupts [NMI], are the exception to this rule. The typical example is the reset interrupt.

NMIs can be useful for implementing things such as a system watchdog, where a NMI is raised periodically and sets some flag that must be acknowledged by the operating system. If the acknowledgement is not seen before the next periodic NMI, then system can be considered to be not making forward progress. Another common usage is for profiling a system. A periodic NMI can be raised and used to evaluate what code the processor is currently running; over time this builds a profile of what code is being run and create a very useful insight into system performance.

Obviously the processor will need to communicate with the peripheral device, and it does this via IO operations. The most common form of IO is so called memory mapped IO where registers on the device are mapped into memory.

This means that to communicate with the device, you need simply read or write to a specific address in memory. TODO: expand

A device requires both input and output to be useful. There are a number of common concepts required for useful communication with peripherals.

An interrupt allows the device to literally interrupt the processor to flag some information. For example, when a key is pressed, an interrupt is generated to deliver the key-press event to the operating system. Each device is assigned an interrupt by some combination of the operating system and BIOS.

Devices are generally connected to an programmable interrupt controller (PIC), a separate chip that is part of the motherboard which buffers and communicates interrupt information to the main processor. Each device has a physical interrupt line between it an one of the PIC's provided by the system. When the device wants to interrupt, it will modify the voltage on this line.

A very broad description of the PIC's role is that it receives this interrupt and converts it to a message for consumption by the main processor. While the exact procedure varies by architecture, the general principle is that the operating system has configured an interrupt descriptor table which pairs each of the possible interrupts with a code address to jump to when the interrupt is received. This is illustrated in Figure 3.6, “Overview of handling an interrupt”.

Writing this interrupt handler is the job of the device driver author in conjunction with the operating system.

Figure 3.6. Overview of handling an interrupt


Most drivers will spilt up handling of interrupts into bottom and top halves. The bottom half will acknowledge the interrupt, queue actions for processing and return the processor to what it was doing quickly. The top half will then run later when the CPU is free and do the more intensive processing. This is to stop an interrupt hogging the entire CPU.

Since an interrupt can happen at any time, it is important that you can return to the running operation when finished handling the interrupt. It is generally the job of the operating system to ensure that upon entry to the interrupt handler, it saves any state; i.e. registers, and restores them when returning from the interrupt handler. In this way, apart from some lost time, the interrupt is completely transparent to whatever happens to be running at the time.

Interrupts v traps and exceptions

While an interrupt is generally associated with an external event from a physical device, the same mechanism is useful for handling internal system operations. For example, if the processor detects conditions such as an access to invalid memory, an attempt to divide-by-zero or an invalid instruction, it can internally raise an exception to be handled by the operating system. It is also the mechanism used to trap into the operating system for system calls, as discussed in the section called “System Calls” and to implement virtual memory, as discussed in Chapter 6, Virtual Memory. Although generated internally rather than from an external source, the principles of asynchronously interrupting the running code remains the same.

There are two main ways of signalling interrupts on a line — level and edge triggered.

Level-triggered interrupts define voltage of the interrupt line being held high to indicate an interrupt is pending. Edge-triggered interrupts detect transitions on the bus; that is when the line voltage goes from low to high. With an edge-triggered interrupt, a square-wave pulse is detected by the PIC as signalling and interrupt has been raised.

The difference is pronounced when devices share an interrupt line. In a level-triggered system, the interrupt line will be high until all devices that have raised an interrupt have been processed and un-asserted their interrupt.

In an edge-triggered system, a pulse on the line will indicate to the PIC that an interrupt has occurred, which it will signal to the operating system for handling. However, if further pulses come in on the already asserted line from another device.

The issue with level-triggered interrupts is that it may require some considerable amount of time to handle an interrupt for a device. During this time, the interrupt line remains high and it is not possible to determine if any other device has raised an interrupt on the line. This means there can be considerable unpredictable latency in servicing interrupts.

With edge-triggered interrupts, a long-running interrupt can be noticed and queued, but other devices sharing the line can still transition (and hence raise interrupts) while this happens. However, this introduces new problems; if two devices interrupt at the same time it may be possible to miss one of the interrupts, or environmental or other interference may create a spurious interrupt which should be ignored.

It is important for the system to be able to mask or prevent interrupts at certain times. Generally, it is possible to put interrupts on hold, but a particular class of interrupts, called non-maskable interrupts (NMI), are the exception to this rule. The typical example is the reset interrupt.

NMIs can be useful for implementing things such as a system watchdog, where a NMI is raised periodically and sets some flag that must be acknowledged by the operating system. If the acknowledgement is not seen before the next periodic NMI, then system can be considered to be not making forward progress. Another common usage is for profiling a system. A periodic NMI can be raised and used to evaluate what code the processor is currently running; over time this builds a profile of what code is being run and create a very useful insight into system performance.

Obviously the processor will need to communicate with the peripheral device, and it does this via IO operations. The most common form of IO is so called memory mapped IO where registers on the device are mapped into memory.

This means that to communicate with the device, you need simply read or write to a specific address in memory. TODO: expand