What is a buffer?
A buffer is as its name suggests an area of memory that is used to store data, usually on a temporary basis prior to processing it. It is used to compensate for timing problems between software modules or subsystems that cannot always guarantee to process every piece of data as it becomes available. It is also used as a collection point for data so that all the relevant information can be collected and organised before processing.
The diagram shows the basic construction of a buffer. It consists of a
block of memory and a pointer that is used to locate the next piece of data to
be accessed or removed from the buffer. There are additional pointers which are
used to control the buffer and prevent data overrun and underrun. An overrun
occurs when the buffer cannot accept any more data. An underrun is caused when
it is asked for data and cannot provide it.
Data is removed by using the pointer to locate the next value and moving
the data from the buffer. The pointer is then moved to the next location by
incrementing its value by the number of bytes or words that have been taken.
One common programming mis-take is to confuse words and bytes. A 32 bit
processor may access a 32 bit word and therefore it would be logical to think
that the pointer is incremented by one. The addressing scheme may use bytes and
therefore the correct increment is four. Adding data is the opposite procedure.
The details on exactly how these proce-dures work determine the buffer type and
its characteristics and are explained later in this chapter.
However, while buffering does undoubtedly offer benefits, they are not
all for free and their use can cause problems. The diagram shows the common
trade-offs that are normally encoun-tered with buffering.
If data is stored in a buffer, then there is normally a delay before the
first and subsequent data is received from the buffer. This delay is known as
the buffer latency and in some systems, it can be a big advantage. In others,
however, its effect is the opposite.
For a real-time system, the buffer latency defines the earliest that
information can be processed and therefore any response to that information
will be delayed by the latency irrespective of how fast or efficient the
processing software and hardware is. If data is buffered so that eight samples
must be received before the first is processed, the real-time response is now
eight times the data rate for a single sample plus the processing time. If the
first sample was a request to stop a machine or ignore the next set of data,
the processing that determines its meaning would occur after the event it was
associated with. In this case, the data that should have been ignored is now in
the buffer and has to be removed.
Latency can also be a big problem for data streams that rely on
real-time to retain their characteristics. For example, digital audio requires
a consistent and regular stream of data to ensure accurate reproduction.
Without this, the audio is distorted and can become unintelligible in the case
of speech. Buffering can help by effectively having some samples in reserve so
that the audio data is always there for decoding or processing. This is fine
except that there is now an initial delay while the buffer fills up. This delay
means an interaction with the stream is difficult as anyone who has had an
international call over a satellite link with the large amount of delay can
vouch for. In addition some systems cannot tolerate delay. Digital telephone
handsets have to demonstrate a very small delay in the audio processing path
which limits the size of any buffering for the digital audio data to less than
four samples. Any higher and the delay caused by buffer latency means that the
phone will fail its type approval.
Latency is not all bad, however, and used in the right amounts can
provide a system that is more tolerant and resilient than one that is not. The
issue is based around how time critical the system is and perhaps more
importantly how deterministic is it.
Consider a system where audio is digitally sampled, fil-tered and
stored. The sampling is performed on a regular basis and the filtering takes
less time than the interval between samples. In this case, it is possible to
build a system that does not need buffering and will have a very low latency.
As each sample is received, it is processed and stored. The latency is the time
to take a single sample.
If the system has other activities and especially if those involve
asynchronous events such as the user pressing a button on the panel, then the
guarantee that all the processing can be completed between samples may no
longer be true. If this dead-line is not made, then a sample may be lost. One
solution to this — there are others such as using a priority system as supplied
by a real-time operating system — is to use a small buffer to temporar-ily
store the data so that it is not lost. By doing this the time constraints on
the processing software are reduced and are more tolerant of other events. This
is, however, at the expense of a slightly increased latency.
One of the concerns with buffers is the memory space that they can take.
With a large system this is not necessarily a problem but with a
microcontroller or a DSP with on-chip memory, this can be an issue when only
small amounts of RAM are available.
There is one other issue concerned with buffers and buffer-ing technique
and that is the complexity of the control structures needed to manage them.
There is a definite trade-off between the control structure and the efficiency
that the buffer can offer in terms of memory utilisation. This is potentially
more important in the region of control and interfacing with interrupts and
other real-time events. For example, a buffer can be created with a simple area
of memory and a single pointer. This is how the frequently used stack is
created. The control associated with the memory — or buffer which is what the
memory really represents
— is a simple register acting as an address pointer. The additional
control that is needed to remember the stacking order and the frame size and
organisation is built into the frame itself and is controlled by the
microprocessor hardware. This additional level of control must be replicated
either in the buffer control software or by the tasks that use the buffer. If a
single task is associated with a buffer, it is straightforward to allow the
task to implement the control. If several tasks use the same buffer, then the
control has to cope with multiple and, possibly, conflicting accesses and while
this can be done by the tasks, it is better to nominate a single entity to
control the buffer. However, the code complexity associated with the buffer has
The code complexity is also dependent on how the buffer is organised. It
is common for multiple pointers to be used along with other data such as the
number of bytes stored and so on. The next section in this chapter will explain
the commonly used buffer structures.